Exemplo n.º 1
0
    def stream_direct(self, _channel_dict, _write_buffer):
        """
        Processes m3u8 interface without using ffmpeg
        """
        self.config = self.db_configdefn.get_config()
        self.channel_dict = _channel_dict
        self.write_buffer = _write_buffer
        duration = 6
        play_queue = OrderedDict()
        self.last_refresh = time.time()
        stream_uri = self.get_stream_uri(_channel_dict)
        if not stream_uri:
            self.logger.warning('Unknown Channel')
            return
        self.logger.debug('M3U8: {}'.format(stream_uri))
        self.file_filter = None
        if self.config[_channel_dict['namespace'].lower(
        )]['player-enable_url_filter']:
            stream_filter = self.config[
                _channel_dict['namespace'].lower()]['player-url_filter']
            if stream_filter is not None:
                self.file_filter = re.compile(stream_filter)
            else:
                self.logger.warning(
                    '[{}]][player-enable_url_filter]'
                    ' enabled but [player-url_filter] not set'.format(
                        _channel_dict['namespace'].lower()))
        if self.config[_channel_dict['namespace'].lower(
        )]['player-enable_pts_filter']:
            self.pts_validation = PTSValidation(self.config, self.channel_dict)

        while True:
            try:
                added = 0
                removed = 0
                playlist = m3u8.load(stream_uri)
                removed += self.remove_from_stream_queue(playlist, play_queue)
                added += self.add_to_stream_queue(playlist, play_queue)
                if added == 0 and duration > 0:
                    time.sleep(duration * 0.3)
                elif self.plugins.plugins[_channel_dict['namespace']].plugin_obj \
                        .is_time_to_refresh_ext(self.last_refresh, _channel_dict['instance']):
                    stream_uri = self.get_stream_uri(_channel_dict)
                    self.logger.debug('M3U8: {}'.format(stream_uri))
                    self.last_refresh = time.time()
                self.play_queue(play_queue)
            except IOError as e:
                # Check we hit a broken pipe when trying to write back to the client
                if e.errno in [
                        errno.EPIPE, errno.ECONNABORTED, errno.ECONNRESET,
                        errno.ECONNREFUSED
                ]:
                    # Normal process.  Client request end of stream
                    self.logger.info(
                        '2. Connection dropped by end device {}'.format(e))
                    break
                else:
                    self.logger.error('{}{}'.format('3 UNEXPECTED EXCEPTION=',
                                                    e))
                    raise
Exemplo n.º 2
0
    def get_channel_uri(self, _channel_id):
        self.logger.info(self.locast.name + ": Getting station info for " + _channel_id)
        stream_url = ''.join([
            'https://api.locastnet.org/api/watch/station/',
            str(_channel_id), '/',
            self.locast_instance.location.latitude, '/',
            self.locast_instance.location.longitude])
        stream_headers = {'Content-Type': 'application/json',
            'authorization': 'Bearer ' + self.locast_instance.token,
            'User-agent': constants.DEFAULT_USER_AGENT}
        req = urllib.request.Request(stream_url, headers=stream_headers)
        with urllib.request.urlopen(req) as resp:
            stream_result = json.load(resp)
        self.logger.debug("Determining best video stream for " + _channel_id + "...")
        bestStream = None

        # find the heighest stream url resolution and save it to the list
        videoUrlM3u = m3u8.load(stream_result['streamUrl'],
            headers={'authorization': 'Bearer ' + self.locast_instance.token,
                'User-agent': constants.DEFAULT_USER_AGENT})
        self.logger.debug("Found " + str(len(videoUrlM3u.playlists)) + " Playlists")

        if len(videoUrlM3u.playlists) > 0:
            for videoStream in videoUrlM3u.playlists:
                if bestStream is None:
                    bestStream = videoStream

                elif ((videoStream.stream_info.resolution[0] > bestStream.stream_info.resolution[0]) and
                      (videoStream.stream_info.resolution[1] > bestStream.stream_info.resolution[1])):
                    bestStream = videoStream

                elif ((videoStream.stream_info.resolution[0] == bestStream.stream_info.resolution[0]) and
                      (videoStream.stream_info.resolution[1] == bestStream.stream_info.resolution[1]) and
                      (videoStream.stream_info.bandwidth > bestStream.stream_info.bandwidth)):
                    bestStream = videoStream

            if bestStream is not None:
                self.logger.debug(_channel_id + " will use " +
                    str(bestStream.stream_info.resolution[0]) + "x" +
                    str(bestStream.stream_info.resolution[1]) +
                    " resolution at " + str(bestStream.stream_info.bandwidth) + "bps")

                return bestStream.absolute_uri

        else:
            self.logger.debug("No variant streams found for this station.  Assuming single stream only.")
            return stream_result['streamUrl']
Exemplo n.º 3
0
    def get_station_stream_uri(self, station_id):
        print("Getting station info for " + station_id + "...")

        try:
            videoUrlReq = urllib.request.Request('https://api.locastnet.org/api/watch/station/' +
                                                 str(station_id) + '/' +
                                                 self.location['latitude'] + '/' +
                                                 self.location['longitude'],
                                                 headers={'Content-Type': 'application/json',
                                                          'authorization': 'Bearer ' + self.current_token,
                                                          'User-agent': self.DEFAULT_USER_AGENT})
            videoUrlOpn = urllib.request.urlopen(videoUrlReq)
            videoUrlRes = json.load(videoUrlOpn)
            videoUrlOpn.close()
        except urllib.error.URLError as urlError:
            print("Error when getting the video URL: " + str(urlError.reason))
            return False
        except urllib.error.HTTPError as httpError:
            print("Error when getting the video URL: " + str(httpError.reason))
            return False
        except:
            videoUrlReqErr = sys.exc_info()[0]

            if hasattr(videoUrlReqErr, 'message'):
                print("Error when getting the video URL: " + videoUrlReqErr.message)
            elif hasattr(videoUrlReqErr, 'reason'):
                print("Error when getting the video URL: " + videoUrlReqErr.reason)
            else:
                print("Error when getting the video URL: " + str(videoUrlReqErr))

            return False

        print("Determining best video stream for " + station_id + "...")

        bestStream = None

        # find the heighest stream url resolution and save it to the list
        videoUrlM3u = m3u8.load(videoUrlRes['streamUrl'], headers={'authorization': 'Bearer ' + self.current_token,
                                                                   'User-agent': self.DEFAULT_USER_AGENT})



        print("Found " + str(len(videoUrlM3u.playlists)) + " Playlists")

        if len(videoUrlM3u.playlists) > 0:
            for videoStream in videoUrlM3u.playlists:
                if bestStream is None:
                    bestStream = videoStream

                elif ((videoStream.stream_info.resolution[0] > bestStream.stream_info.resolution[0]) and
                      (videoStream.stream_info.resolution[1] > bestStream.stream_info.resolution[1])):
                    bestStream = videoStream

                elif ((videoStream.stream_info.resolution[0] == bestStream.stream_info.resolution[0]) and
                      (videoStream.stream_info.resolution[1] == bestStream.stream_info.resolution[1]) and
                      (videoStream.stream_info.bandwidth > bestStream.stream_info.bandwidth)):
                    bestStream = videoStream


            if bestStream is not None:
                print(station_id + " will use " +
                      str(bestStream.stream_info.resolution[0]) + "x" + str(bestStream.stream_info.resolution[1]) +
                      " resolution at " + str(bestStream.stream_info.bandwidth) + "bps")

                return bestStream.absolute_uri

        else:
            print("No variant streams found for this station.  Assuming single stream only.")
            return videoUrlRes['streamUrl']
Exemplo n.º 4
0
    def stream_direct(self, sid, _namespace, _instance, station_list):
        segments = OrderedDict()
        duration = 1
        file_filter = None
        self.last_refresh = time.time()
        stream_uri = self.get_stream_uri(sid, _namespace, _instance)
        if not stream_uri:
            self.do_response(
                501, 'text/html',
                tvh_templates['htmlError'].format('501 - Unknown channel'))
            return
        self.logger.debug('M3U8: {}'.format(stream_uri))
        if self.config['player']['stream_filter'] is not None:
            file_filter = re.compile(self.config['player']['stream_filter'])
        while True:
            try:
                added = 0
                removed = 0
                playlist = m3u8.load(stream_uri)
                for segment_dict in list(segments.keys()):
                    is_found = False
                    for segment_m3u8 in playlist.segments:
                        uri = segment_m3u8.absolute_uri
                        if segment_dict == uri:
                            is_found = True
                            break
                    if not is_found:
                        del segments[segment_dict]
                        removed += 1
                        self.logger.debug(
                            f"Removed {segment_dict} from play queue")
                        continue
                    else:
                        break

                for m3u8_segment in playlist.segments:
                    uri = m3u8_segment.absolute_uri
                    if uri not in segments:
                        played = False
                        if file_filter is not None:
                            m = file_filter.match(uri)
                            if m:
                                played = True
                        segments[uri] = {
                            'played': played,
                            'duration': m3u8_segment.duration
                        }
                        self.logger.debug(f"Added {uri} to play queue")
                        added += 1

                if added == 0 and duration > 0:
                    time.sleep(duration * 0.3)
                elif self.is_time_to_refresh():
                    stream_uri = self.get_stream_uri(sid, _namespace,
                                                     _instance)
                    self.logger.debug('M3U8: {}'.format(stream_uri))
                    self.last_refresh = time.time()

                for uri, data in segments.items():
                    if not data["played"]:
                        start_download = datetime.datetime.utcnow()
                        chunk = requests.get(uri).content
                        end_download = datetime.datetime.utcnow()
                        download_secs = (end_download -
                                         start_download).total_seconds()
                        data['played'] = True
                        if not chunk:
                            self.logger.warning(
                                f"Segment {uri} not available. Skipping..")
                            continue
                        atsc_msg = ATSCMsg()
                        chunk_updated = atsc_msg.update_sdt_names(
                            chunk[:80], b'Locast',
                            self.set_service_name(station_list, sid).encode())
                        chunk = chunk_updated + chunk[80:]
                        duration = data['duration']
                        runtime = (datetime.datetime.utcnow() -
                                   start_download).total_seconds()
                        target_diff = 0.3 * duration
                        wait = target_diff - runtime
                        self.logger.info(f"Serving {uri} ({duration}s)")
                        self.write_buffer(chunk)
                        if wait > 0:
                            time.sleep(wait)
            except IOError as e:
                # Check we hit a broken pipe when trying to write back to the client
                if e.errno in [
                        errno.EPIPE, errno.ECONNABORTED, errno.ECONNRESET,
                        errno.ECONNREFUSED
                ]:
                    # Normal process.  Client request end of stream
                    self.logger.info(
                        '2. Connection dropped by end device {}'.format(e))
                    break
                else:
                    self.logger.error('{}{}'.format('3 UNEXPECTED EXCEPTION=',
                                                    e))
                    raise
            except Exception as e:
                traceback.print_exc()
                break