def test_date_tag_bad_value(self): date = GLib.Date.new_dmy(7, 1, 10000) taglist = self.make_taglist(Gst.TAG_DATE, [date]) result = tags.convert_taglist(taglist) assert len(result[Gst.TAG_DATE]) == 0
def test_date_tag(self): date = GLib.Date.new_dmy(7, 1, 2014) taglist = self.make_taglist(Gst.TAG_DATE, [date]) result = tags.convert_taglist(taglist) assert isinstance(result[Gst.TAG_DATE][0], compat.text_type) assert result[Gst.TAG_DATE][0] == '2014-01-07'
def test_string_tag(self): taglist = self.make_taglist(Gst.TAG_ARTIST, [b'ABBA', b'ACDC']) result = tags.convert_taglist(taglist) assert isinstance(result[Gst.TAG_ARTIST][0], compat.text_type) assert result[Gst.TAG_ARTIST][0] == 'ABBA' assert isinstance(result[Gst.TAG_ARTIST][1], compat.text_type) assert result[Gst.TAG_ARTIST][1] == 'ACDC'
def test_date_time_tag(self): taglist = self.make_taglist(Gst.TAG_DATE_TIME, [ Gst.DateTime.new_from_iso8601_string(b'2014-01-07 14:13:12') ]) result = tags.convert_taglist(taglist) assert isinstance(result[Gst.TAG_DATE_TIME][0], compat.text_type) assert result[Gst.TAG_DATE_TIME][0] == '2014-01-07T14:13:12Z'
def test_string_tag(self): taglist = self.make_taglist(Gst.TAG_ARTIST, [b"ABBA", b"ACDC"]) result = tags.convert_taglist(taglist) assert isinstance(result[Gst.TAG_ARTIST][0], str) assert result[Gst.TAG_ARTIST][0] == "ABBA" assert isinstance(result[Gst.TAG_ARTIST][1], str) assert result[Gst.TAG_ARTIST][1] == "ACDC"
def test_date_time_tag(self): taglist = self.make_taglist( Gst.TAG_DATE_TIME, [Gst.DateTime.new_from_iso8601_string(b'2014-01-07 14:13:12')]) result = tags.convert_taglist(taglist) assert isinstance(result[Gst.TAG_DATE_TIME][0], compat.text_type) assert result[Gst.TAG_DATE_TIME][0] == '2014-01-07T14:13:12Z'
def test_date_time_tag(self): taglist = self.make_taglist( Gst.TAG_DATE_TIME, [Gst.DateTime.new_from_iso8601_string("2014-01-07 14:13:12")], ) result = tags.convert_taglist(taglist) assert isinstance(result[Gst.TAG_DATE_TIME][0], str) assert result[Gst.TAG_DATE_TIME][0] == "2014-01-07T14:13:12Z"
def _process(pipeline, timeout_ms): bus = pipeline.get_bus() tags = {} mime = None have_audio = False missing_message = None types = ( Gst.MessageType.ELEMENT | Gst.MessageType.APPLICATION | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.ASYNC_DONE | Gst.MessageType.TAG ) timeout = timeout_ms previous = int(time.time() * 1000) while timeout > 0: message = bus.timed_pop_filtered(timeout * Gst.MSECOND, types) if message is None: break elif message.type == Gst.MessageType.ELEMENT: if GstPbutils.is_missing_plugin_message(message): missing_message = message elif message.type == Gst.MessageType.APPLICATION: if message.get_structure().get_name() == "have-type": mime = message.get_structure().get_value("caps").get_name() if mime and (mime.startswith("text/") or mime == "application/xml"): return tags, mime, have_audio elif message.get_structure().get_name() == "have-audio": have_audio = True elif message.type == Gst.MessageType.ERROR: error = encoding.locale_decode(message.parse_error()[0]) if missing_message and not mime: caps = missing_message.get_structure().get_value("detail") mime = caps.get_structure(0).get_name() return tags, mime, have_audio raise exceptions.ScannerError(error) elif message.type == Gst.MessageType.EOS: return tags, mime, have_audio elif message.type == Gst.MessageType.ASYNC_DONE: if message.src == pipeline: return tags, mime, have_audio elif message.type == Gst.MessageType.TAG: taglist = message.parse_tag() # Note that this will only keep the last tag. tags.update(tags_lib.convert_taglist(taglist)) now = int(time.time() * 1000) timeout -= now - previous previous = now raise exceptions.ScannerError("Timeout after %dms" % timeout_ms)
def _process(pipeline, timeout_ms): bus = pipeline.get_bus() tags = {} mime = None have_audio = False missing_message = None types = (Gst.MessageType.ELEMENT | Gst.MessageType.APPLICATION | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.ASYNC_DONE | Gst.MessageType.TAG) timeout = timeout_ms previous = int(time.time() * 1000) while timeout > 0: message = bus.timed_pop_filtered(timeout * Gst.MSECOND, types) if message is None: break elif message.type == Gst.MessageType.ELEMENT: if GstPbutils.is_missing_plugin_message(message): missing_message = message elif message.type == Gst.MessageType.APPLICATION: if message.get_structure().get_name() == 'have-type': mime = message.get_structure().get_value('caps').get_name() if mime and (mime.startswith('text/') or mime == 'application/xml'): return tags, mime, have_audio elif message.get_structure().get_name() == 'have-audio': have_audio = True elif message.type == Gst.MessageType.ERROR: error = encoding.locale_decode(message.parse_error()[0]) if missing_message and not mime: caps = missing_message.get_structure().get_value('detail') mime = caps.get_structure(0).get_name() return tags, mime, have_audio raise exceptions.ScannerError(error) elif message.type == Gst.MessageType.EOS: return tags, mime, have_audio elif message.type == Gst.MessageType.ASYNC_DONE: if message.src == pipeline: return tags, mime, have_audio elif message.type == Gst.MessageType.TAG: taglist = message.parse_tag() # Note that this will only keep the last tag. tags.update(tags_lib.convert_taglist(taglist)) now = int(time.time() * 1000) timeout -= now - previous previous = now raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)
def on_tag(self, taglist): tags = tags_lib.convert_taglist(taglist) gst_logger.debug('Got TAG bus message: tags=%r', dict(tags)) # Postpone emitting tags until stream start. if self._audio._pending_tags is not None: self._audio._pending_tags.update(tags) return # TODO: Add proper tests for only emitting changed tags. unique = object() changed = [] for key, value in tags.items(): # Update any tags that changed, and store changed keys. if self._audio._tags.get(key, unique) != value: self._audio._tags[key] = value changed.append(key) if changed: logger.debug('Audio event: tags_changed(tags=%r)', changed) AudioListener.send('tags_changed', tags=changed)
def on_tag(self, taglist): tags = tags_lib.convert_taglist(taglist) gst_logger.debug('Got TAG bus message: tags=%r', dict(tags)) self._audio._tags.update(tags) logger.debug('Audio event: tags_changed(tags=%r)', tags.keys()) AudioListener.send('tags_changed', tags=tags.keys())
def test_integer_tag(self): taglist = self.make_taglist(Gst.TAG_BITRATE, [17]) result = tags.convert_taglist(taglist) assert result[Gst.TAG_BITRATE][0] == 17
def _process(pipeline, timeout_ms): bus = pipeline.get_bus() tags = {} mime = None have_audio = False missing_message = None duration = None types = ( Gst.MessageType.ELEMENT | Gst.MessageType.APPLICATION | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.ASYNC_DONE | Gst.MessageType.DURATION_CHANGED | Gst.MessageType.TAG ) timeout = timeout_ms start = int(time.time() * 1000) while timeout > 0: msg = bus.timed_pop_filtered(timeout * Gst.MSECOND, types) if msg is None: break if logger.isEnabledFor(log.TRACE_LOG_LEVEL) and msg.get_structure(): debug_text = msg.get_structure().to_string() if len(debug_text) > 77: debug_text = debug_text[:77] + '...' _trace('element %s: %s', msg.src.get_name(), debug_text) if msg.type == Gst.MessageType.ELEMENT: if GstPbutils.is_missing_plugin_message(msg): missing_message = msg elif msg.type == Gst.MessageType.APPLICATION: if msg.get_structure().get_name() == 'have-type': mime = msg.get_structure().get_value('caps').get_name() if mime and ( mime.startswith('text/') or mime == 'application/xml'): return tags, mime, have_audio, duration elif msg.get_structure().get_name() == 'have-audio': have_audio = True elif msg.type == Gst.MessageType.ERROR: error = encoding.locale_decode(msg.parse_error()[0]) if missing_message and not mime: caps = missing_message.get_structure().get_value('detail') mime = caps.get_structure(0).get_name() return tags, mime, have_audio, duration raise exceptions.ScannerError(error) elif msg.type == Gst.MessageType.EOS: return tags, mime, have_audio, duration elif msg.type == Gst.MessageType.ASYNC_DONE: success, duration = _query_duration(pipeline) if tags and success: return tags, mime, have_audio, duration # Don't try workaround for non-seekable sources such as mmssrc: if not _query_seekable(pipeline): return tags, mime, have_audio, duration # Workaround for upstream bug which causes tags/duration to arrive # after pre-roll. We get around this by starting to play the track # and then waiting for a duration change. # https://bugzilla.gnome.org/show_bug.cgi?id=763553 logger.debug('Using workaround for duration missing before play.') result = pipeline.set_state(Gst.State.PLAYING) if result == Gst.StateChangeReturn.FAILURE: return tags, mime, have_audio, duration elif msg.type == Gst.MessageType.DURATION_CHANGED and tags: # VBR formats sometimes seem to not have a duration by the time we # go back to paused. So just try to get it right away. success, duration = _query_duration(pipeline) pipeline.set_state(Gst.State.PAUSED) if success: return tags, mime, have_audio, duration elif msg.type == Gst.MessageType.TAG: taglist = msg.parse_tag() # Note that this will only keep the last tag. tags.update(tags_lib.convert_taglist(taglist)) timeout = timeout_ms - (int(time.time() * 1000) - start) raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)
def _process(pipeline, timeout_ms): bus = pipeline.get_bus() tags = {} mime = None have_audio = False missing_message = None duration = None types = (Gst.MessageType.ELEMENT | Gst.MessageType.APPLICATION | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.ASYNC_DONE | Gst.MessageType.DURATION_CHANGED | Gst.MessageType.TAG) timeout = timeout_ms start = int(time.time() * 1000) while timeout > 0: message = bus.timed_pop_filtered(timeout * Gst.MSECOND, types) if message is None: break elif message.type == Gst.MessageType.ELEMENT: if GstPbutils.is_missing_plugin_message(message): missing_message = message elif message.type == Gst.MessageType.APPLICATION: if message.get_structure().get_name() == 'have-type': mime = message.get_structure().get_value('caps').get_name() if mime and (mime.startswith('text/') or mime == 'application/xml'): return tags, mime, have_audio, duration elif message.get_structure().get_name() == 'have-audio': have_audio = True elif message.type == Gst.MessageType.ERROR: error = encoding.locale_decode(message.parse_error()[0]) if missing_message and not mime: caps = missing_message.get_structure().get_value('detail') mime = caps.get_structure(0).get_name() return tags, mime, have_audio, duration raise exceptions.ScannerError(error) elif message.type == Gst.MessageType.EOS: return tags, mime, have_audio, duration elif message.type == Gst.MessageType.ASYNC_DONE: success, duration = _query_duration(pipeline) if tags and success: return tags, mime, have_audio, duration # Workaround for upstream bug which causes tags/duration to arrive # after pre-roll. We get around this by starting to play the track # and then waiting for a duration change. # https://bugzilla.gnome.org/show_bug.cgi?id=763553 result = pipeline.set_state(Gst.State.PLAYING) if result == Gst.StateChangeReturn.FAILURE: return tags, mime, have_audio, duration elif message.type == Gst.MessageType.DURATION_CHANGED: # duration will be read after ASYNC_DONE received; for now # just give it a non-None value to flag that we have a duration: duration = 0 elif message.type == Gst.MessageType.TAG: taglist = message.parse_tag() # Note that this will only keep the last tag. tags.update(tags_lib.convert_taglist(taglist)) timeout = timeout_ms - (int(time.time() * 1000) - start) # workaround for https://bugzilla.gnome.org/show_bug.cgi?id=763553: # if we got what we want then stop playing (and wait for ASYNC_DONE) if tags and duration is not None: pipeline.set_state(Gst.State.PAUSED) raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)
def _process(pipeline, timeout_ms): bus = pipeline.get_bus() tags = {} mime = None have_audio = False missing_message = None duration = None types = (Gst.MessageType.ELEMENT | Gst.MessageType.APPLICATION | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.ASYNC_DONE | Gst.MessageType.DURATION_CHANGED | Gst.MessageType.TAG) timeout = timeout_ms start = int(time.time() * 1000) while timeout > 0: msg = bus.timed_pop_filtered(timeout * Gst.MSECOND, types) if msg is None: break if logger.isEnabledFor(log.TRACE_LOG_LEVEL) and msg.get_structure(): debug_text = msg.get_structure().to_string() if len(debug_text) > 77: debug_text = debug_text[:77] + '...' _trace('element %s: %s', msg.src.get_name(), debug_text) if msg.type == Gst.MessageType.ELEMENT: if GstPbutils.is_missing_plugin_message(msg): missing_message = msg elif msg.type == Gst.MessageType.APPLICATION: if msg.get_structure().get_name() == 'have-type': mime = msg.get_structure().get_value('caps').get_name() if mime and (mime.startswith('text/') or mime == 'application/xml'): return tags, mime, have_audio, duration elif msg.get_structure().get_name() == 'have-audio': have_audio = True elif msg.type == Gst.MessageType.ERROR: error = encoding.locale_decode(msg.parse_error()[0]) if missing_message and not mime: caps = missing_message.get_structure().get_value('detail') mime = caps.get_structure(0).get_name() return tags, mime, have_audio, duration raise exceptions.ScannerError(error) elif msg.type == Gst.MessageType.EOS: return tags, mime, have_audio, duration elif msg.type == Gst.MessageType.ASYNC_DONE: success, duration = _query_duration(pipeline) if tags and success: return tags, mime, have_audio, duration # Don't try workaround for non-seekable sources such as mmssrc: if not _query_seekable(pipeline): return tags, mime, have_audio, duration # Workaround for upstream bug which causes tags/duration to arrive # after pre-roll. We get around this by starting to play the track # and then waiting for a duration change. # https://bugzilla.gnome.org/show_bug.cgi?id=763553 logger.debug('Using workaround for duration missing before play.') result = pipeline.set_state(Gst.State.PLAYING) if result == Gst.StateChangeReturn.FAILURE: return tags, mime, have_audio, duration elif msg.type == Gst.MessageType.DURATION_CHANGED and tags: # VBR formats sometimes seem to not have a duration by the time we # go back to paused. So just try to get it right away. success, duration = _query_duration(pipeline) pipeline.set_state(Gst.State.PAUSED) if success: return tags, mime, have_audio, duration elif msg.type == Gst.MessageType.TAG: taglist = msg.parse_tag() # Note that this will only keep the last tag. tags.update(tags_lib.convert_taglist(taglist)) timeout = timeout_ms - (int(time.time() * 1000) - start) raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)
def _process(pipeline, timeout_ms): bus = pipeline.get_bus() tags = {} mime = None have_audio = False missing_message = None duration = None types = ( Gst.MessageType.ELEMENT | Gst.MessageType.APPLICATION | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.ASYNC_DONE | Gst.MessageType.DURATION_CHANGED | Gst.MessageType.TAG ) timeout = timeout_ms start = int(time.time() * 1000) while timeout > 0: message = bus.timed_pop_filtered(timeout * Gst.MSECOND, types) if message is None: break elif message.type == Gst.MessageType.ELEMENT: if GstPbutils.is_missing_plugin_message(message): missing_message = message elif message.type == Gst.MessageType.APPLICATION: if message.get_structure().get_name() == 'have-type': mime = message.get_structure().get_value('caps').get_name() if mime and ( mime.startswith('text/') or mime == 'application/xml'): return tags, mime, have_audio, duration elif message.get_structure().get_name() == 'have-audio': have_audio = True elif message.type == Gst.MessageType.ERROR: error = encoding.locale_decode(message.parse_error()[0]) if missing_message and not mime: caps = missing_message.get_structure().get_value('detail') mime = caps.get_structure(0).get_name() return tags, mime, have_audio, duration raise exceptions.ScannerError(error) elif message.type == Gst.MessageType.EOS: return tags, mime, have_audio, duration elif message.type == Gst.MessageType.ASYNC_DONE: success, duration = _query_duration(pipeline) if tags and success: return tags, mime, have_audio, duration # Workaround for upstream bug which causes tags/duration to arrive # after pre-roll. We get around this by starting to play the track # and then waiting for a duration change. # https://bugzilla.gnome.org/show_bug.cgi?id=763553 result = pipeline.set_state(Gst.State.PLAYING) if result == Gst.StateChangeReturn.FAILURE: return tags, mime, have_audio, duration elif message.type == Gst.MessageType.DURATION_CHANGED: # duration will be read after ASYNC_DONE received; for now # just give it a non-None value to flag that we have a duration: duration = 0 elif message.type == Gst.MessageType.TAG: taglist = message.parse_tag() # Note that this will only keep the last tag. tags.update(tags_lib.convert_taglist(taglist)) timeout = timeout_ms - (int(time.time() * 1000) - start) # workaround for https://bugzilla.gnome.org/show_bug.cgi?id=763553: # if we got what we want then stop playing (and wait for ASYNC_DONE) if tags and duration is not None: pipeline.set_state(Gst.State.PAUSED) raise exceptions.ScannerError('Timeout after %dms' % timeout_ms)