def __init__(self, stream): self._stream = RtmpStreamReader(stream) self._skip_meta = self._skip_head = self._skip_magic_head = False #self._skip_audio_tag = self._skip_video_tag = False self._skip_audio_tag = True self._skip_video_tag = False self._buf = [] self.onMetaData = None
def __init__(self, fn, mode=True): stream = file(fn) self._fd = os.open(fn, os.O_RDONLY) self._reader = RtmpStreamReader(stream) self._pre_pos = 0 self._skip_first_video_tag = False self._first_video_tag = None self._head = None self._keyframes = [] self._skip_video_tag = False self._skip_first_audio_tag = False self._first_audio_tag = None self._head_dead_line = 0 super(FlvFragment, self).__init__(mode=mode) self._swig = 0
def __init__(self, data_in=None, step_length=1, channel=None, start_chunk_id='-0x1', delay_seconds = 0, enable_backswing=0): """ @data_in: Data source. @step_length: The max length of time slices can play. @channel: The name of the data source. @start_chunk_id: Number of the starting chunk, starting from the current time by default. """ if not channel: raise FragmentError('Channenl is required.') self._channel = channel if not data_in: data_in = sys.stdin elif data_in.startswith("rtmp://"): import librtmp conn = librtmp.RTMP(data_in, live=True) # Attempt to connect conn.connect() # Get a file-like object to access to the stream stream = conn.create_stream() # Read 1024 bytes of data data_in = stream self._buf = [] #store tag temporary. self._id_generator = id_generator(int(start_chunk_id, 16)) self._stream = RtmpStreamReader(data_in) #data source reader. self._step_length = step_length self.delay_seconds = delay_seconds self._seq = 0 #sequence number of tag. self._chunk_seq = 0 #sequence number of chunk. self._skip_meta = self._skip_head = self._skip_magic_head = False self._flv_head_no = 0 self._swing = 0 # swing self._head = None self._skip_audio_tag = self._skip_video_tag = False self._enable_backswing = enable_backswing self._do_sleep = False self._has_head = False self._meta_tag = None
class VodFragment(object): def __init__(self, stream): self._stream = RtmpStreamReader(stream) self._skip_meta = self._skip_head = self._skip_magic_head = False #self._skip_audio_tag = self._skip_video_tag = False self._skip_audio_tag = True self._skip_video_tag = False self._buf = [] self.onMetaData = None def handle_magic_head(self, tag): """ Catch the first audio tag and video tag in the stream. """ if not self._skip_video_tag and tag.type == VIDEO_TAG: self._first_video_tag = tag self._skip_video_tag = True if not self._skip_audio_tag and tag.type == AUDIO_TAG: self._first_audio_tag = tag self._skip_audio_tag = True return (not(self._skip_audio_tag and self._skip_video_tag)) #return (not(self._skip_audio_tag or self._skip_video_tag)) def slice_head(self): for tag in self._stream: last_position = self._stream.position() self._buf.append(tag) if isinstance(tag, Tag): if tag.type == 18 and self.onMetaData: self.onMetaData(tag) if not self.handle_magic_head(tag): return last_position return last_position def flush(self): buf = '' for tag in self._buf: if isinstance(tag, Head): buf += pack_head(tag) else: buf += tag.pack() return buf
class VodFragment(object): def __init__(self, stream): self._stream = RtmpStreamReader(stream) self._skip_meta = self._skip_head = self._skip_magic_head = False #self._skip_audio_tag = self._skip_video_tag = False self._skip_audio_tag = True self._skip_video_tag = False self._buf = [] self.onMetaData = None def handle_magic_head(self, tag): """ Catch the first audio tag and video tag in the stream. """ if not self._skip_video_tag and tag.type == VIDEO_TAG: self._first_video_tag = tag self._skip_video_tag = True if not self._skip_audio_tag and tag.type == AUDIO_TAG: self._first_audio_tag = tag self._skip_audio_tag = True return (not (self._skip_audio_tag and self._skip_video_tag)) #return (not(self._skip_audio_tag or self._skip_video_tag)) def slice_head(self): for tag in self._stream: last_position = self._stream.position() self._buf.append(tag) if isinstance(tag, Tag): if tag.type == 18 and self.onMetaData: self.onMetaData(tag) if not self.handle_magic_head(tag): return last_position return last_position def flush(self): buf = '' for tag in self._buf: if isinstance(tag, Head): buf += pack_head(tag) else: buf += tag.pack() return buf
class FlvFragment(Fragment): def __init__(self, fn, mode=True): stream = file(fn) self._fd = os.open(fn, os.O_RDONLY) self._reader = RtmpStreamReader(stream) self._pre_pos = 0 self._skip_first_video_tag = False self._first_video_tag = None self._head = None self._keyframes = [] self._skip_video_tag = False self._skip_first_audio_tag = False self._first_audio_tag = None self._head_dead_line = 0 super(FlvFragment, self).__init__(mode=mode) self._swig = 0 def position(self): return self._reader.position() def get_keyframes(self): return self._keyframes keyframes = property(get_keyframes) def size(self): return len(self._keyframes) def append_keyframe(self, tag): self._keyframes.append((self._pre_pos, tag.timestamp)) self._ptr = len(self._keyframes) - 1 self._swig = 0 #init loop def incr(self, step): if self.pointer == self.size() - 1: self._ptr += 1 self._swig = 0 else: self._swig += step span = self.span() print 'cacle %d' % (span, ) if self._swig > span: self._ptr += 1 print "increase ptr %d" % self._ptr self._swig -= span def span(self): if self.pointer >= self.size() - 1: return 0 p = self.pointer a = self._keyframes[p][1] b = self._keyframes[p + 1][1] return ((b - a) / 1000) def advance(self): """ Get a slice of CPU time to run. """ in_bytes = self._pre_pos for tag in self._reader: if isinstance(tag, Tag): # skip the Metadata in flv stream. if not self.handle_magic_head(tag): if tag.type == VIDEO_TAG and tag.is_keyframe: self.append_keyframe(tag) self._pre_pos = self.position() in_bytes = self._pre_pos - in_bytes if in_bytes > 0: self.active() else: self.inactive() def handle_magic_head(self, tag): """ Catch the first audio tag and video tag in the stream. """ if not self._skip_video_tag and tag.type == VIDEO_TAG: self._first_video_tag = tag self._head_dead_line = self.position() self._skip_video_tag = True if not self._skip_first_audio_tag and tag.type == AUDIO_TAG: self._first_audio_tag = tag self._head_dead_line = self.position() self._skip_first_audio_tag = True return (not (self._skip_video_tag and self._skip_first_audio_tag)) def do(self, start=-1, offset=None, flag=False, keyIndex=None): begin = end = 0 current = keyIndex and keyIndex or self.pointer if start < 0: #从关键帧开始取数据 begin = self._keyframes[current][0] else: begin = start end = offset and min(begin + offset, self.position()) or self.position() data_length = end - begin data = '' if data_length > 0: os.lseek(self._fd, begin, os.SEEK_SET) data = os.read(self._fd, data_length) else: raise OSError("Reach file end.") body = '' if flag: if not self._head and self._head_dead_line: os.lseek(self._fd, 0, os.SEEK_SET) self._head = os.read(self._fd, self._head_dead_line) body += struct.pack('>H%ds' % (len(self._head), ), len(self._head), self._head) else: body += struct.pack('>H', 0) body += struct.pack('>QIH', begin, data_length, config.NEXT_REQ_DELAY_TIME) #filepositions temp = '' for (k, t) in self._keyframes: if k >= begin: temp += struct.pack('>QI', k, t) if k > end: break body += struct.pack('>I', len(temp) / 12) + temp # insert head length body = struct.pack('>I', len(body)) + body + data return body def close(self): os.close(self._fd) self._reader.close()
class Fragment(object): """ Slice flv data stream into pieces. """ def __init__(self, data_in=None, step_length=1, channel=None, start_chunk_id='-0x1', delay_seconds = 0, enable_backswing=0): """ @data_in: Data source. @step_length: The max length of time slices can play. @channel: The name of the data source. @start_chunk_id: Number of the starting chunk, starting from the current time by default. """ if not channel: raise FragmentError('Channenl is required.') self._channel = channel if not data_in: data_in = sys.stdin elif data_in.startswith("rtmp://"): import librtmp conn = librtmp.RTMP(data_in, live=True) # Attempt to connect conn.connect() # Get a file-like object to access to the stream stream = conn.create_stream() # Read 1024 bytes of data data_in = stream self._buf = [] #store tag temporary. self._id_generator = id_generator(int(start_chunk_id, 16)) self._stream = RtmpStreamReader(data_in) #data source reader. self._step_length = step_length self.delay_seconds = delay_seconds self._seq = 0 #sequence number of tag. self._chunk_seq = 0 #sequence number of chunk. self._skip_meta = self._skip_head = self._skip_magic_head = False self._flv_head_no = 0 self._swing = 0 # swing self._head = None self._skip_audio_tag = self._skip_video_tag = False self._enable_backswing = enable_backswing self._do_sleep = False self._has_head = False self._meta_tag = None def get_sleep_mode(self): return self._do_sleep def set_sleep_mode(self, value): self._do_sleep = value sleep_mode = property(get_sleep_mode, set_sleep_mode) def get_enable_backswing(self): return self._enable_backswing def set_enable_backswing(self, value): self._enable_backswing = value enable_backswing = property(get_enable_backswing, set_enable_backswing) def get_channel(self): """ Return the channel name. """ return self._channel channel = property(get_channel) def get_auto_step_length(self): """ Return the step length in 微秒. """ return (self._step_length * 1000) + self.backswing auto_step_length = property(get_auto_step_length) def get_backswing(self): """ 反向摆动值. """ return (self._swing * (-1)) backswing = property(get_backswing) def get_length_of_play_time(self): """ Caculate the length of play time avilable in temporary cache. """ if len(self._buf) > 1: return self._buf[-1].timestamp - self._buf[0].timestamp return 0 length_of_play_time = property(get_length_of_play_time) def get_stream_time(self): """ Get the time of play stream. """ return self._start_time + (self._chunk_seq * self._step_length) stream_time = property(get_stream_time) def measure(self): """ Determine whether chunk generated at this time. If does, call method cut. """ if self.length_of_play_time > self.auto_step_length: self._cut() def _cut(self): """ Do generate a chunk from the temporary cache array. And then, clear the cache. """ now = time.time() # Get time of now in seconds. chunk_id = self._id_generator() #Generate a number of chunk from the growth. self._chunk_seq += 1 #Increase the number of chunk sequence. self.on_chunk_generated(chunk_id, self._buf) self._clear() if self.enable_backswing: self._swing = now - self.stream_time #Compute the value of deference between the current time and the stream time. def _clear(self): """ Clear the temporary cache. """ self._buf = [] def handle_magic_head(self, tag): """ Catch the first audio tag and video tag in the stream. """ #print("skip_audio_tag:%s skip_video_tab:%s" % (self._skip_audio_tag, self._skip_video_tag)) if self._has_head: return False if not self._skip_video_tag and tag.type == VIDEO_TAG: self._first_video_tag = tag self._skip_video_tag = True if not self._skip_audio_tag and tag.type == AUDIO_TAG: self._first_audio_tag = tag self._skip_audio_tag = True #return (not(self._skip_audio_tag or self._skip_video_tag)) if self._head.audio_flag == 0: if self._skip_video_tag: self._has_head = True return False else: if self._skip_audio_tag and self._skip_video_tag: self._has_head = True return False return True def advance(self): """ Get a slice of CPU time to run. """ self._start_time = time.time() #Start time in seconds. if not self._skip_head: #First read from stream. try: tag = self._stream.next() if not isinstance(tag, Head): raise FragmentError('Flv stream head not found!') self._head = tag self._skip_head = True except StopIteration: return for tag in self._stream: self._seq += 1 # Increase the number of Tag sequence. if isinstance(tag, Tag): # skip the Metadata in flv stream. if tag.type == META_DATA: self._meta_tag = tag #print(tag) <Tag(type:meta data,size:121,ext:0,ts:0)> 121 + 11 = 132 continue if not self.handle_magic_head(tag): self._buf.append(tag) self.measure() # Judge slice chunk. else: #todo: May be ingored raise FragmentError('Receive another flv head from stream!') if self._do_sleep: time.sleep(0.01) def on_chunk_generated(self, chunk_id, tag_array): """ Implemented by subclass. """ raise NotImplementedError('on_chunk_generated')
class FlvFragment(Fragment): def __init__(self, fn, mode=True): stream = file(fn) self._fd = os.open(fn, os.O_RDONLY) self._reader = RtmpStreamReader(stream) self._pre_pos = 0 self._skip_first_video_tag = False self._first_video_tag = None self._head = None self._keyframes = [] self._skip_video_tag = False self._skip_first_audio_tag = False self._first_audio_tag = None self._head_dead_line = 0 super(FlvFragment, self).__init__(mode=mode) self._swig = 0 def position(self): return self._reader.position() def get_keyframes(self): return self._keyframes keyframes = property(get_keyframes) def size(self): return len(self._keyframes) def append_keyframe(self, tag): self._keyframes.append((self._pre_pos, tag.timestamp)) self._ptr = len(self._keyframes) - 1 self._swig = 0 #init loop def incr(self, step): if self.pointer == self.size() - 1: self._ptr += 1 self._swig = 0 else: self._swig += step span = self.span() print 'cacle %d' % (span,) if self._swig > span: self._ptr += 1 print "increase ptr %d" % self._ptr self._swig -= span def span(self): if self.pointer >= self.size() - 1: return 0 p = self.pointer a = self._keyframes[p][1] b = self._keyframes[p+1][1] return ((b - a)/1000) def advance(self): """ Get a slice of CPU time to run. """ in_bytes = self._pre_pos for tag in self._reader: if isinstance(tag, Tag): # skip the Metadata in flv stream. if not self.handle_magic_head(tag): if tag.type == VIDEO_TAG and tag.is_keyframe: self.append_keyframe(tag) self._pre_pos = self.position() in_bytes = self._pre_pos - in_bytes if in_bytes > 0: self.active() else: self.inactive() def handle_magic_head(self, tag): """ Catch the first audio tag and video tag in the stream. """ if not self._skip_video_tag and tag.type == VIDEO_TAG: self._first_video_tag = tag self._head_dead_line = self.position() self._skip_video_tag = True if not self._skip_first_audio_tag and tag.type == AUDIO_TAG: self._first_audio_tag = tag self._head_dead_line = self.position() self._skip_first_audio_tag = True return (not(self._skip_video_tag and self._skip_first_audio_tag)) def do(self, start=-1, offset=None, flag=False, keyIndex=None): begin = end = 0 current = keyIndex and keyIndex or self.pointer if start < 0: #从关键帧开始取数据 begin = self._keyframes[current][0] else: begin = start end = offset and min(begin + offset, self.position()) or self.position() data_length = end - begin data = '' if data_length > 0: os.lseek(self._fd, begin, os.SEEK_SET) data = os.read(self._fd, data_length) else: raise OSError("Reach file end.") body = '' if flag: if not self._head and self._head_dead_line: os.lseek(self._fd, 0, os.SEEK_SET) self._head = os.read(self._fd, self._head_dead_line) body += struct.pack('>H%ds'%(len(self._head),), len(self._head), self._head) else: body += struct.pack('>H', 0) body += struct.pack('>QIH', begin, data_length, config.NEXT_REQ_DELAY_TIME) #filepositions temp = '' for (k,t) in self._keyframes: if k >= begin: temp += struct.pack('>QI', k, t) if k > end: break body += struct.pack('>I', len(temp)/12) + temp # insert head length body = struct.pack('>I', len(body)) + body + data return body def close(self): os.close(self._fd) self._reader.close()