Beispiel #1
0
        internal void step(RecorderHub hub)
        {
            if (lastCheckTask != null && !lastCheckTask.IsCompleted)
            {
                return;
            }

            var recording = hub.getRecording(roomName);

            if (recording?.isRunning == true)
            {
                return;
            }

            var now = UnixTime.now;

            var lastRecordEnd = recording?.timeExit ?? 0L;

            if (now - lastRecordEnd < 300000L)
            {
                // 前回の録画が終わってから5分間は 最も短い間隔でチェックする
                if (now < lastCheckTime + 5000L)
                {
                    return;
                }
            }
            else
            {
                // 最低限10秒は待つ
                if (now < lastCheckTime + 10000L)
                {
                    return;
                }

                // 分数が0,5,10, ... に近いなら間隔を短くする
                var dtNow    = now.toDateTime();
                var x        = Math.Abs((dtNow.Minute * 60 + dtNow.Second) % 300 - 30) / 30;
                var interval = x switch
                {
                    0 => UnixTime.second1 * 10,
                    1 => UnixTime.second1 * 20,
                    8 => UnixTime.second1 * 20,
                    _ => UnixTime.second1 * 30,
                };
                if (now < lastCheckTime + interval)
                {
                    return;
                }
            }

            lastCheckTime = now;
            lastCheckTask = Task.Run(async() => await check(hub));
        }
Beispiel #2
0
        internal String getFolder(RecorderHub hub)
        {
            var dir = rePathDelimiter.Replace($"{hub.saveDir}/{roomName}", "/");

            try {
                Directory.CreateDirectory(dir);
            } catch (Exception ex) {
                log.e(ex, "CreateDirectory() failed.");
            }

            return(dir);
        }
Beispiel #3
0
        public Recording(RecorderHub hub, String roomName, String url, String folder)
        {
            this.url      = url;
            this.roomName = roomName;

            var timeStr = UnixTime.now.formatFileTime();

            // todo 保存フォルダの設定
            var file  = $"{folder}/{timeStr}-{roomName}.ts";
            var count = 1;

            while (File.Exists(file))
            {
                ++count;
                file = $"{folder}/{timeStr}-{roomName}-{++count}.ts";
            }

            var p = new Process();

            p.StartInfo.CreateNoWindow         = true;
            p.StartInfo.FileName               = hub.ffmpegPath;
            p.StartInfo.Arguments              = $"{Config.ffmpegOptions} -user_agent \"{Config.userAgent}\" -i \"{url}\" -c copy \"{ file}\"";
            p.StartInfo.UseShellExecute        = false; // require to read output
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError  = true;
            p.EnableRaisingEvents              = true;
            p.Exited += (sender, args) => {
                timeExit = UnixTime.now;
                log.d($"{roomName}: recording process was exited.");
                hub.mainWindow.play(NotificationSound.recordingEnd);

                hub.showStatus();
            };
            p.Start();
            Task.Run(async() => await readStream(p.StandardOutput));
            Task.Run(async() => await readStream(p.StandardError));
            this.process = p;
            hub.showStatus();
            hub.mainWindow.play(NotificationSound.recordingStart);
        }
Beispiel #4
0
        async Task check(RecorderHub hub)
        {
            try {
                if (!File.Exists(hub.ffmpegPath))
                {
                    log.e($"ffmpegPath '${hub.ffmpegPath}' is not valid.");
                }

                var folder = getFolder(hub);
                if (!Directory.Exists(folder))
                {
                    log.e($"folder '${folder}' is not valid.");
                }

                // オンライブ部屋一覧にあるストリーミング情報はあまり信用できないので読み直す
                var url     = $"{Config.URL_TOP}api/live/streaming_url?room_id={roomId}&ignore_low_stream=1&_={UnixTime.now / 1000L}";
                var request = new HttpRequestMessage(HttpMethod.Get, url);
                request.Headers.Add("Accept", "application/json");
                var response = await Config.httpClient.SendAsync(request).ConfigureAwait(false);

                var now     = UnixTime.now;
                var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                var root = JToken.Parse(content);
                var streaming_url_list = root.Value <JArray>("streaming_url_list");

                if (streaming_url_list == null)
                {
                    return;
                }

                if (streaming_url_list.Count == 0)
                {
                    log.d($"{roomName}: there is streaming_url_list, but it's empty.");
                    return;
                }

                var list = new List <StreamingInfo>();
                foreach (JObject src in streaming_url_list)
                {
                    try {
                        var si = new StreamingInfo(
                            src.Value <String>("url") !,
                            src.Value <String>("type") !,
                            src.Value <Boolean>("is_default"),
                            src.Value <Int64>("quality")
                            );
                        if (si.type != "hls")
                        {
                            log.d($"{roomName}: not hls. type={si.type}");
                            continue;
                        }
                        list.Add(si);
                    } catch (Exception ex) {
                        log.e(ex, $"{roomName}: StreamingInfo parse failed.");
                    }
                }

                if (list.Count == 0)
                {
                    log.d($"{roomName}: StreamingInfo list is empty. original size={streaming_url_list.Count}");
                    return;
                }
                list.Sort();

                var streamUrl = list[0].url;

                var recording = hub.getRecording(roomName);
                if (recording?.isRunning == true && recording?.url == streamUrl)
                {
                    log.d($"{roomName}: already recording {streamUrl}");
                    return;
                }

                // streamUrl が決定しても動画が開始しているとは限らない
                request  = new HttpRequestMessage(HttpMethod.Get, streamUrl);
                response = await Config.httpClient.SendAsync(request).ConfigureAwait(false);

                var code = response.codeInt();
                if (!response.IsSuccessStatusCode)
                {
                    log.d($"{roomName}: {code} {response.ReasonPhrase} {streamUrl}");
                    return;
                }

                content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                var    reLineFeed = new Regex("[\x0d\x0a]+");
                String?chunk      = null;
                foreach (var a in reLineFeed.Split(content))
                {
                    var line = a.Trim();
                    if (line.Length == 0 || line.StartsWith("#"))
                    {
                        continue;
                    }
                    log.d(line);
                    if (chunk == null)
                    {
                        chunk = line;
                    }
                }
                if (chunk == null)
                {
                    log.d($"{roomName}: missing chunk in playlsit.");
                    return;
                }

                if (!Uri.TryCreate(new Uri(streamUrl), chunk, out var chunkUrl))
                {
                    log.d($"{roomName}: can't combile chunk url. {chunk}");
                    return;
                }
                request  = new HttpRequestMessage(HttpMethod.Head, chunkUrl);
                response = await Config.httpClient.SendAsync(request).ConfigureAwait(false);

                code = response.codeInt();
                if (!response.IsSuccessStatusCode)
                {
                    log.d($"{roomName}: {code} {response.ReasonPhrase} {chunkUrl}");
                    return;
                }

                if (hub.isDisposed)
                {
                    log.d($"{roomName}: hub was disposed.");
                    return;
                }
                log.d($"{roomName}: recording start!");
                hub.setRecodring(roomName, streamUrl, folder);
            } catch (Exception ex) {
                log.e(ex, $"{roomName}: check failed.");
            }
        }