Exemple #1
0
async def test_stream_ended(hass, stream_worker_sync):
    """Test hls stream packets ended."""
    await async_setup_component(hass, "stream", {"stream": {}})

    stream_worker_sync.pause()

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    track = stream.add_provider("hls")

    # Request stream
    request_stream(hass, source)

    # Run it dead
    while True:
        segment = await track.recv()
        if segment is None:
            break
        segments = segment.sequence
        # Allow worker to finalize once enough of the stream is been consumed
        if segments > 1:
            stream_worker_sync.resume()

    assert segments > 1
    assert not track.get_segment()

    # Stop stream, if it hasn't quit already
    stream.stop()
Exemple #2
0
async def test_stream_keepalive(hass):
    """Test hls stream retries the stream when keepalive=True."""
    await async_setup_component(hass, "stream", {"stream": {}})

    # Setup demo HLS track
    source = "test_stream_keepalive_source"
    stream = preload_stream(hass, source)
    track = stream.add_provider("hls")
    track.num_segments = 2

    cur_time = 0

    def time_side_effect():
        nonlocal cur_time
        if cur_time >= 80:
            stream.keepalive = False  # Thread should exit and be joinable.
        cur_time += 40
        return cur_time

    with patch("av.open") as av_open, patch(
            "homeassistant.components.stream.worker.time"
    ) as mock_time, patch(
            "homeassistant.components.stream.worker.STREAM_RESTART_INCREMENT",
            0):
        av_open.side_effect = av.error.InvalidDataError(-2, "error")
        mock_time.time.side_effect = time_side_effect
        # Request stream
        request_stream(hass, source, keepalive=True)
        stream._thread.join()
        stream._thread = None
        assert av_open.call_count == 2

    # Stop stream, if it hasn't quit already
    stream.stop()
Exemple #3
0
async def test_stream_ended(hass):
    """Test hls stream packets ended."""
    await async_setup_component(hass, 'stream', {
        'stream': {}
    })

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    track = stream.add_provider('hls')
    track.num_segments = 2

    # Request stream
    request_stream(hass, source)

    # Run it dead
    segments = 0
    while await track.recv() is not None:
        segments += 1

    assert segments == 3
    assert not track.get_segment()

    # Stop stream, if it hasn't quit already
    stream.stop()
Exemple #4
0
async def async_setup_platform(hass, config, async_add_entities,
                               discovery_info=None):
    """Set up a generic IP Camera."""
    async_add_entities([GenericCamera(hass, config)])

    if config[CONF_PRELOAD_STREAM] and config[CONF_STREAM_SOURCE]:
        request_stream(hass, config[CONF_STREAM_SOURCE],
                       keepalive=True, options=dict(config[CONF_STREAM_OPTIONS]))
Exemple #5
0
    async def preload_stream(hass, _):
        for camera in component.entities:
            camera_prefs = prefs.get(camera.entity_id)
            if not camera_prefs.preload_stream:
                continue

            async with async_timeout.timeout(10):
                source = await camera.stream_source()

            if not source:
                continue

            request_stream(hass, source, keepalive=True)
Exemple #6
0
async def async_handle_play_stream_service(camera, service_call):
    """Handle play stream services calls."""
    if not camera.stream_source:
        raise HomeAssistantError(
            "{} does not support play stream service".format(camera.entity_id))

    hass = camera.hass
    camera_prefs = hass.data[DATA_CAMERA_PREFS].get(camera.entity_id)
    fmt = service_call.data[ATTR_FORMAT]
    entity_ids = service_call.data[ATTR_MEDIA_PLAYER]

    url = request_stream(hass,
                         camera.stream_source,
                         fmt=fmt,
                         keepalive=camera_prefs.preload_stream)
    data = {
        ATTR_ENTITY_ID: entity_ids,
        ATTR_MEDIA_CONTENT_ID: "{}{}".format(hass.config.api.base_url, url),
        ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[fmt]
    }

    await hass.services.async_call(DOMAIN_MP,
                                   SERVICE_PLAY_MEDIA,
                                   data,
                                   blocking=True,
                                   context=service_call.context)
Exemple #7
0
async def async_handle_play_stream_service(camera, service_call):
    """Handle play stream services calls."""
    async with async_timeout.timeout(10):
        source = await camera.stream_source()

    if not source:
        raise HomeAssistantError(
            f"{camera.entity_id} does not support play stream service")

    hass = camera.hass
    camera_prefs = hass.data[DATA_CAMERA_PREFS].get(camera.entity_id)
    fmt = service_call.data[ATTR_FORMAT]
    entity_ids = service_call.data[ATTR_MEDIA_PLAYER]

    url = request_stream(
        hass,
        source,
        fmt=fmt,
        keepalive=camera_prefs.preload_stream,
        options=camera.stream_options,
    )
    data = {
        ATTR_ENTITY_ID: entity_ids,
        ATTR_MEDIA_CONTENT_ID: f"{get_url(hass)}{url}",
        ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[fmt],
    }

    await hass.services.async_call(DOMAIN_MP,
                                   SERVICE_PLAY_MEDIA,
                                   data,
                                   blocking=True,
                                   context=service_call.context)
Exemple #8
0
async def test_stream_timeout(hass, hass_client):
    """Test hls stream timeout."""
    await async_setup_component(hass, 'stream', {'stream': {}})

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    stream.add_provider('hls')

    # Request stream
    url = request_stream(hass, source)

    http_client = await hass_client()

    # Fetch playlist
    parsed_url = urlparse(url)
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Wait a minute
    future = dt_util.utcnow() + timedelta(minutes=1)
    async_fire_time_changed(hass, future)

    # Fetch again to reset timer
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Wait 5 minutes
    future = dt_util.utcnow() + timedelta(minutes=5)
    async_fire_time_changed(hass, future)

    # Ensure playlist not accessable
    fail_response = await http_client.get(parsed_url.path)
    assert fail_response.status == 404
Exemple #9
0
async def ws_camera_stream(hass, connection, msg):
    """Handle get camera stream websocket command.

    Async friendly.
    """
    try:
        entity_id = msg["entity_id"]
        camera = _get_camera_from_entity_id(hass, entity_id)
        camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id)

        async with async_timeout.timeout(10):
            source = await camera.stream_source()

        if not source:
            raise HomeAssistantError(
                f"{camera.entity_id} does not support play stream service")

        fmt = msg["format"]
        url = request_stream(
            hass,
            source,
            fmt=fmt,
            keepalive=camera_prefs.preload_stream,
            options=camera.stream_options,
        )
        connection.send_result(msg["id"], {"url": url})
    except HomeAssistantError as ex:
        _LOGGER.error("Error requesting stream: %s", ex)
        connection.send_error(msg["id"], "start_stream_failed", str(ex))
    except asyncio.TimeoutError:
        _LOGGER.error("Timeout getting stream source")
        connection.send_error(msg["id"], "start_stream_failed",
                              "Timeout getting stream source")
Exemple #10
0
async def async_request_stream(hass, entity_id, fmt):
    """Request a stream for a camera entity."""
    camera = _get_camera_from_entity_id(hass, entity_id)
    camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id)

    if not camera.stream_source:
        raise HomeAssistantError("{} does not support play stream service"
                                 .format(camera.entity_id))

    return request_stream(hass, camera.stream_source, fmt=fmt,
                          keepalive=camera_prefs.preload_stream)
Exemple #11
0
async def async_request_stream(hass, entity_id, fmt):
    """Request a stream for a camera entity."""
    camera = _get_camera_from_entity_id(hass, entity_id)
    camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id)

    if not camera.stream_source:
        raise HomeAssistantError(
            "{} does not support play stream service".format(camera.entity_id))

    return request_stream(hass,
                          camera.stream_source,
                          fmt=fmt,
                          keepalive=camera_prefs.preload_stream)
Exemple #12
0
async def async_request_stream(hass, entity_id, fmt):
    """Request a stream for a camera entity."""
    camera = _get_camera_from_entity_id(hass, entity_id)
    camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id)

    async with async_timeout.timeout(10):
        source = await camera.stream_source()

    if not source:
        raise HomeAssistantError(
            f"{camera.entity_id} does not support play stream service"
        )

    return request_stream(hass, source, fmt=fmt, keepalive=camera_prefs.preload_stream)
Exemple #13
0
async def test_stream_ended(hass):
    """Test hls stream packets ended."""
    await async_setup_component(hass, 'stream', {'stream': {}})

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    track = stream.add_provider('hls')
    track.num_segments = 2

    # Request stream
    request_stream(hass, source)

    # Run it dead
    segments = 0
    while await track.recv() is not None:
        segments += 1

    assert segments > 1
    assert not track.get_segment()

    # Stop stream, if it hasn't quit already
    stream.stop()
async def test_stream_ended(hass):
    """Test hls stream packets ended."""
    await async_setup_component(hass, "stream", {"stream": {}})

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    track = stream.add_provider("hls")

    # Request stream
    request_stream(hass, source)

    # Run it dead
    while True:
        segment = await track.recv()
        if segment is None:
            break
        segments = segment.sequence

    assert segments > 1
    assert not track.get_segment()

    # Stop stream, if it hasn't quit already
    stream.stop()
Exemple #15
0
async def test_hls_stream(hass, hass_client, stream_worker_sync):
    """
    Test hls stream.

    Purposefully not mocking anything here to test full
    integration with the stream component.
    """
    await async_setup_component(hass, "stream", {"stream": {}})

    stream_worker_sync.pause()

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    stream.add_provider("hls")

    # Request stream
    url = request_stream(hass, source)

    http_client = await hass_client()

    # Fetch playlist
    parsed_url = urlparse(url)
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Fetch init
    playlist = await playlist_response.text()
    playlist_url = "/".join(parsed_url.path.split("/")[:-1])
    init_url = playlist_url + "/init.mp4"
    init_response = await http_client.get(init_url)
    assert init_response.status == 200

    # Fetch segment
    playlist = await playlist_response.text()
    playlist_url = "/".join(parsed_url.path.split("/")[:-1])
    segment_url = playlist_url + "/" + playlist.splitlines()[-1]
    segment_response = await http_client.get(segment_url)
    assert segment_response.status == 200

    stream_worker_sync.resume()

    # Stop stream, if it hasn't quit already
    stream.stop()

    # Ensure playlist not accessible after stream ends
    fail_response = await http_client.get(parsed_url.path)
    assert fail_response.status == HTTP_NOT_FOUND
async def ws_camera_stream(hass, connection, msg):
    """Handle get camera stream websocket command.

    Async friendly.
    """
    try:
        camera = _get_camera_from_entity_id(hass, msg['entity_id'])

        if not camera.stream_source:
            raise HomeAssistantError("{} does not support play stream service"
                                     .format(camera.entity_id))

        fmt = msg['format']
        url = request_stream(hass, camera.stream_source, fmt=fmt)
        connection.send_result(msg['id'], {'url': url})
    except HomeAssistantError as ex:
        _LOGGER.error(ex)
        connection.send_error(
            msg['id'], 'start_stream_failed', str(ex))
Exemple #17
0
async def async_handle_play_stream_service(camera, service_call):
    """Handle play stream services calls."""
    if not camera.stream_source:
        raise HomeAssistantError("{} does not support play stream service"
                                 .format(camera.entity_id))

    hass = camera.hass
    fmt = service_call.data[ATTR_FORMAT]
    entity_ids = service_call.data[ATTR_MEDIA_PLAYER]

    url = request_stream(hass, camera.stream_source, fmt=fmt)
    data = {
        ATTR_ENTITY_ID: entity_ids,
        ATTR_MEDIA_CONTENT_ID: "{}{}".format(hass.config.api.base_url, url),
        ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[fmt]
    }

    await hass.services.async_call(
        DOMAIN_MP, SERVICE_PLAY_MEDIA, data,
        blocking=True, context=service_call.context)
Exemple #18
0
async def ws_camera_stream(hass, connection, msg):
    """Handle get camera stream websocket command.

    Async friendly.
    """
    try:
        entity_id = msg['entity_id']
        camera = _get_camera_from_entity_id(hass, entity_id)
        camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id)

        if not camera.stream_source:
            raise HomeAssistantError("{} does not support play stream service"
                                     .format(camera.entity_id))

        fmt = msg['format']
        url = request_stream(hass, camera.stream_source, fmt=fmt,
                             keepalive=camera_prefs.preload_stream)
        connection.send_result(msg['id'], {'url': url})
    except HomeAssistantError as ex:
        _LOGGER.error(ex)
        connection.send_error(
            msg['id'], 'start_stream_failed', str(ex))
Exemple #19
0
async def test_hls_stream(hass, hass_client):
    """
    Test hls stream.

    Purposefully not mocking anything here to test full
    integration with the stream component.
    """
    await async_setup_component(hass, 'stream', {
        'stream': {}
    })

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    stream.add_provider('hls')

    # Request stream
    url = request_stream(hass, source)

    http_client = await hass_client()

    # Fetch playlist
    parsed_url = urlparse(url)
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Fetch segment
    playlist = await playlist_response.text()
    playlist_url = '/'.join(parsed_url.path.split('/')[:-1])
    segment_url = playlist_url + playlist.splitlines()[-1][1:]
    segment_response = await http_client.get(segment_url)
    assert segment_response.status == 200

    # Stop stream, if it hasn't quit already
    stream.stop()

    # Ensure playlist not accessable after stream ends
    fail_response = await http_client.get(parsed_url.path)
    assert fail_response.status == 404
Exemple #20
0
async def test_hls_stream(hass, hass_client):
    """
    Test hls stream.

    Purposefully not mocking anything here to test full
    integration with the stream component.
    """
    await async_setup_component(hass, 'stream', {'stream': {}})

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    stream.add_provider('hls')

    # Request stream
    url = request_stream(hass, source)

    http_client = await hass_client()

    # Fetch playlist
    parsed_url = urlparse(url)
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Fetch segment
    playlist = await playlist_response.text()
    playlist_url = '/'.join(parsed_url.path.split('/')[:-1])
    segment_url = playlist_url + playlist.splitlines()[-1][1:]
    segment_response = await http_client.get(segment_url)
    assert segment_response.status == 200

    # Stop stream, if it hasn't quit already
    stream.stop()

    # Ensure playlist not accessable after stream ends
    fail_response = await http_client.get(parsed_url.path)
    assert fail_response.status == 404
Exemple #21
0
async def ws_camera_stream(hass, connection, msg):
    """Handle get camera stream websocket command.

    Async friendly.
    """
    try:
        entity_id = msg['entity_id']
        camera = _get_camera_from_entity_id(hass, entity_id)
        camera_prefs = hass.data[DATA_CAMERA_PREFS].get(entity_id)

        if not camera.stream_source:
            raise HomeAssistantError(
                "{} does not support play stream service".format(
                    camera.entity_id))

        fmt = msg['format']
        url = request_stream(hass,
                             camera.stream_source,
                             fmt=fmt,
                             keepalive=camera_prefs.preload_stream)
        connection.send_result(msg['id'], {'url': url})
    except HomeAssistantError as ex:
        _LOGGER.error(ex)
        connection.send_error(msg['id'], 'start_stream_failed', str(ex))
Exemple #22
0
async def test_stream_timeout(hass, hass_client):
    """Test hls stream timeout."""
    await async_setup_component(hass, 'stream', {
        'stream': {}
    })

    # Setup demo HLS track
    source = generate_h264_video()
    stream = preload_stream(hass, source)
    stream.add_provider('hls')

    # Request stream
    url = request_stream(hass, source)

    http_client = await hass_client()

    # Fetch playlist
    parsed_url = urlparse(url)
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Wait a minute
    future = dt_util.utcnow() + timedelta(minutes=1)
    async_fire_time_changed(hass, future)

    # Fetch again to reset timer
    playlist_response = await http_client.get(parsed_url.path)
    assert playlist_response.status == 200

    # Wait 5 minutes
    future = dt_util.utcnow() + timedelta(minutes=5)
    async_fire_time_changed(hass, future)

    # Ensure playlist not accessable
    fail_response = await http_client.get(parsed_url.path)
    assert fail_response.status == 404
Exemple #23
0
 def preload_stream(event):
     for camera in component.entities:
         camera_prefs = prefs.get(camera.entity_id)
         if camera.stream_source and camera_prefs.preload_stream:
             request_stream(hass, camera.stream_source, keepalive=True)
Exemple #24
0
 def preload_stream(event):
     for camera in component.entities:
         camera_prefs = prefs.get(camera.entity_id)
         if camera.stream_source and camera_prefs.preload_stream:
             request_stream(hass, camera.stream_source, keepalive=True)
Exemple #25
0
async def async_handle_play_stream_service(camera, service_call):
    """Handle play stream services calls."""
    async with async_timeout.timeout(10):
        source = await camera.stream_source()

    if not source:
        raise HomeAssistantError(
            f"{camera.entity_id} does not support play stream service")

    hass = camera.hass
    camera_prefs = hass.data[DATA_CAMERA_PREFS].get(camera.entity_id)
    fmt = service_call.data[ATTR_FORMAT]
    entity_ids = service_call.data[ATTR_MEDIA_PLAYER]

    url = request_stream(
        hass,
        source,
        fmt=fmt,
        keepalive=camera_prefs.preload_stream,
        options=camera.stream_options,
    )
    data = {
        ATTR_MEDIA_CONTENT_ID: f"{get_url(hass)}{url}",
        ATTR_MEDIA_CONTENT_TYPE: FORMAT_CONTENT_TYPE[fmt],
    }

    # It is required to send a different payload for cast media players
    cast_entity_ids = [
        entity for entity, source in entity_sources(hass).items()
        if entity in entity_ids and source["domain"] == "cast"
    ]
    other_entity_ids = list(set(entity_ids) - set(cast_entity_ids))

    if cast_entity_ids:
        await hass.services.async_call(
            DOMAIN_MP,
            SERVICE_PLAY_MEDIA,
            {
                ATTR_ENTITY_ID: cast_entity_ids,
                **data,
                ATTR_MEDIA_EXTRA: {
                    "stream_type": "LIVE",
                    "media_info": {
                        "hlsVideoSegmentFormat": "fmp4",
                    },
                },
            },
            blocking=True,
            context=service_call.context,
        )

    if other_entity_ids:
        await hass.services.async_call(
            DOMAIN_MP,
            SERVICE_PLAY_MEDIA,
            {
                ATTR_ENTITY_ID: other_entity_ids,
                **data,
            },
            blocking=True,
            context=service_call.context,
        )