Disable audio stream when ADTS AAC detected (#47441)

* Disable audio stream when ADTS AAC detected

* Use context manager for memoryview

* Fix tests

* Add test

* Fix tests

* Change FakePacket bytearray size to 3
This commit is contained in:
uvjustin 2021-03-06 23:40:49 +08:00 committed by GitHub
parent 2f9d03d115
commit 14f85d8731
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 42 additions and 5 deletions

View File

@ -208,6 +208,16 @@ def stream_worker(source, options, segment_buffer, quit_event):
missing_dts += 1 missing_dts += 1
continue continue
if packet.stream == audio_stream: if packet.stream == audio_stream:
# detect ADTS AAC and disable audio
if audio_stream.codec.name == "aac" and packet.size > 2:
with memoryview(packet) as packet_view:
if packet_view[0] == 0xFF and packet_view[1] & 0xF0 == 0xF0:
_LOGGER.warning(
"ADTS AAC detected - disabling audio stream"
)
container_packets = container.demux(video_stream)
audio_stream = None
continue
found_audio = True found_audio = True
elif ( elif (
segment_start_pts is None segment_start_pts is None

View File

@ -57,6 +57,11 @@ class FakePyAvStream:
self.time_base = fractions.Fraction(1, rate) self.time_base = fractions.Fraction(1, rate)
self.profile = "ignored-profile" self.profile = "ignored-profile"
class FakeCodec:
name = "aac"
self.codec = FakeCodec()
VIDEO_STREAM = FakePyAvStream(VIDEO_STREAM_FORMAT, VIDEO_FRAME_RATE) VIDEO_STREAM = FakePyAvStream(VIDEO_STREAM_FORMAT, VIDEO_FRAME_RATE)
AUDIO_STREAM = FakePyAvStream(AUDIO_STREAM_FORMAT, AUDIO_SAMPLE_RATE) AUDIO_STREAM = FakePyAvStream(AUDIO_STREAM_FORMAT, AUDIO_SAMPLE_RATE)
@ -87,13 +92,18 @@ class PacketSequence:
raise StopIteration raise StopIteration
self.packet += 1 self.packet += 1
class FakePacket: class FakePacket(bytearray):
# Be a bytearray so that memoryview works
def __init__(self):
super().__init__(3)
time_base = fractions.Fraction(1, VIDEO_FRAME_RATE) time_base = fractions.Fraction(1, VIDEO_FRAME_RATE)
dts = self.packet * PACKET_DURATION / time_base dts = self.packet * PACKET_DURATION / time_base
pts = self.packet * PACKET_DURATION / time_base pts = self.packet * PACKET_DURATION / time_base
duration = PACKET_DURATION / time_base duration = PACKET_DURATION / time_base
stream = VIDEO_STREAM stream = VIDEO_STREAM
is_keyframe = True is_keyframe = True
size = 3
return FakePacket() return FakePacket()
@ -107,8 +117,8 @@ class FakePyAvContainer:
self.packets = PacketSequence(0) self.packets = PacketSequence(0)
class FakePyAvStreams: class FakePyAvStreams:
video = video_stream video = [video_stream] if video_stream else []
audio = audio_stream audio = [audio_stream] if audio_stream else []
self.streams = FakePyAvStreams() self.streams = FakePyAvStreams()
@ -171,8 +181,8 @@ class MockPyAv:
def __init__(self, video=True, audio=False): def __init__(self, video=True, audio=False):
"""Initialize the MockPyAv.""" """Initialize the MockPyAv."""
video_stream = [VIDEO_STREAM] if video else [] video_stream = VIDEO_STREAM if video else None
audio_stream = [AUDIO_STREAM] if audio else [] audio_stream = AUDIO_STREAM if audio else None
self.container = FakePyAvContainer( self.container = FakePyAvContainer(
video_stream=video_stream, audio_stream=audio_stream video_stream=video_stream, audio_stream=audio_stream
) )
@ -413,6 +423,23 @@ async def test_audio_packets_not_found(hass):
assert len(decoded_stream.audio_packets) == 0 assert len(decoded_stream.audio_packets) == 0
async def test_adts_aac_audio(hass):
"""Set up an ADTS AAC audio stream and disable audio."""
py_av = MockPyAv(audio=True)
num_packets = PACKETS_TO_WAIT_FOR_AUDIO + 1
packets = list(PacketSequence(num_packets))
packets[1].stream = AUDIO_STREAM
packets[1].dts = packets[0].dts / VIDEO_FRAME_RATE * AUDIO_SAMPLE_RATE
packets[1].pts = packets[0].pts / VIDEO_FRAME_RATE * AUDIO_SAMPLE_RATE
# The following is packet data is a sign of ADTS AAC
packets[1][0] = 255
packets[1][1] = 241
decoded_stream = await async_decode_stream(hass, iter(packets), py_av=py_av)
assert len(decoded_stream.audio_packets) == 0
async def test_audio_is_first_packet(hass): async def test_audio_is_first_packet(hass):
"""Set up an audio stream and audio packet is the first packet in the stream.""" """Set up an audio stream and audio packet is the first packet in the stream."""
py_av = MockPyAv(audio=True) py_av = MockPyAv(audio=True)