2018-03-12 18:57:20 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.ComponentModel;
|
2018-03-13 13:21:01 +08:00
|
|
|
|
using System.Diagnostics;
|
2018-03-18 18:55:28 +08:00
|
|
|
|
using System.IO;
|
2018-03-13 13:21:01 +08:00
|
|
|
|
using System.Linq;
|
2018-03-13 14:23:53 +08:00
|
|
|
|
using System.Net;
|
2018-03-12 18:57:20 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
using BililiveRecorder.FlvProcessor;
|
|
|
|
|
|
|
|
|
|
namespace BililiveRecorder.Core
|
|
|
|
|
{
|
|
|
|
|
public class RecordedRoom : INotifyPropertyChanged
|
|
|
|
|
{
|
2018-03-15 21:55:01 +08:00
|
|
|
|
public int roomID { get; private set; }
|
|
|
|
|
public RoomInfo roomInfo { get; private set; }
|
|
|
|
|
|
|
|
|
|
public RecordStatus Status;
|
2018-03-12 18:57:20 +08:00
|
|
|
|
|
2018-03-13 14:23:53 +08:00
|
|
|
|
public StreamMonitor streamMonitor;
|
2018-03-12 18:57:20 +08:00
|
|
|
|
public FlvStreamProcessor Processor; // FlvProcessor
|
2018-03-13 13:21:01 +08:00
|
|
|
|
public readonly ObservableCollection<FlvClipProcessor> Clips = new ObservableCollection<FlvClipProcessor>();
|
2018-03-13 14:23:53 +08:00
|
|
|
|
private HttpWebRequest webRequest;
|
2018-03-13 13:21:01 +08:00
|
|
|
|
|
|
|
|
|
public RecordedRoom()
|
|
|
|
|
{
|
|
|
|
|
Processor.BlockProcessed += Processor_BlockProcessed;
|
2018-03-13 14:23:53 +08:00
|
|
|
|
streamMonitor.StreamStatusChanged += StreamMonitor_StreamStatusChanged;
|
2018-03-15 21:55:01 +08:00
|
|
|
|
|
|
|
|
|
UpdateRoomInfo();
|
2018-03-13 14:23:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void StreamMonitor_StreamStatusChanged(object sender, StreamStatusChangedArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.status.isStreaming)
|
|
|
|
|
{
|
|
|
|
|
// TODO: 失败重试逻辑 & 掉线重连逻辑
|
|
|
|
|
_StartRecord();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-15 21:55:01 +08:00
|
|
|
|
public void StartRecord()
|
2018-03-13 14:23:53 +08:00
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
2018-03-13 13:21:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-03-15 21:55:01 +08:00
|
|
|
|
private void _StartRecord()
|
|
|
|
|
{
|
|
|
|
|
// throw new NotImplementedException();
|
|
|
|
|
if (webRequest != null)
|
|
|
|
|
{
|
|
|
|
|
//TODO: cleanup
|
|
|
|
|
webRequest = null;
|
|
|
|
|
}
|
|
|
|
|
if (Processor != null)
|
|
|
|
|
{
|
|
|
|
|
//TODO: cleanup
|
|
|
|
|
Processor = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string flv_path = BililiveAPI.GetPlayUrl(roomInfo.RealRoomid);
|
|
|
|
|
|
|
|
|
|
webRequest = WebRequest.CreateHttp(flv_path);
|
|
|
|
|
_SetupFlvRequest(webRequest);
|
|
|
|
|
HttpWebResponse response = webRequest.GetResponse() as HttpWebResponse;
|
|
|
|
|
if (response.StatusCode != HttpStatusCode.OK)
|
|
|
|
|
{
|
|
|
|
|
//TODO: Log
|
|
|
|
|
response.Close();
|
|
|
|
|
webRequest = null;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-03-18 18:55:28 +08:00
|
|
|
|
Stream flvStream = response.GetResponseStream();
|
|
|
|
|
const int BUF_SIZE = 1024 * 8;// 8 KiB
|
|
|
|
|
byte[] buffer = new byte[BUF_SIZE];
|
2018-03-15 21:55:01 +08:00
|
|
|
|
|
2018-03-18 18:55:28 +08:00
|
|
|
|
AsyncCallback callback = null;
|
|
|
|
|
|
|
|
|
|
callback = ar =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
int bytesRead = flvStream.EndRead(ar);
|
|
|
|
|
|
|
|
|
|
if (bytesRead == 0)
|
|
|
|
|
{
|
|
|
|
|
// TODO: connection closed
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (bytesRead != buffer.Length)
|
|
|
|
|
{
|
|
|
|
|
Processor.AddBytes(buffer.Take(bytesRead).ToArray());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Processor.AddBytes(buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Console.Write('#');
|
|
|
|
|
|
|
|
|
|
flvStream.BeginRead(buffer, 0, BUF_SIZE, callback, null);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
throw; //TODO
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
flvStream.BeginRead(buffer, 0, BUF_SIZE, callback, null);
|
2018-03-15 21:55:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void _SetupFlvRequest(HttpWebRequest r)
|
|
|
|
|
{
|
|
|
|
|
r.Accept = "*/*";
|
|
|
|
|
r.AllowAutoRedirect = true;
|
|
|
|
|
r.Connection = "keep-alive";
|
|
|
|
|
r.Referer = "https://live.bilibili.com";
|
|
|
|
|
r.Headers["Origin"] = "https://live.bilibili.com";
|
|
|
|
|
r.UserAgent = "Mozilla/5.0 BililiveRecorder/0.0.0.0 (+https://github.com/Bililive/BililiveRecorder;bliverec@genteure.com)";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StartWebRequest()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FinishWebRequest(IAsyncResult result)
|
|
|
|
|
{
|
|
|
|
|
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool UpdateRoomInfo()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
roomInfo = BililiveAPI.GetRoomInfo(roomID);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine(ex.ToString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-13 13:21:01 +08:00
|
|
|
|
// Called by API or GUI
|
|
|
|
|
public void Clip()
|
|
|
|
|
{
|
|
|
|
|
var clip = Processor.Clip();
|
|
|
|
|
// TODO: 多个线程同时运行,这个位置有可能会导致 Clip 丢数据
|
|
|
|
|
// 考虑在此处加锁, Clip 操作时停止向主 Processor 添加数据
|
|
|
|
|
clip.ClipFinalized += CallBack_ClipFinalized;
|
|
|
|
|
Clips.Add(clip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CallBack_ClipFinalized(object sender, ClipFinalizedArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (Clips.Remove(e.ClipProcessor))
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine("Clip Finalized");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine("Warning! Clip Finalized but was not in Collection.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Processor_BlockProcessed(object sender, BlockProcessedArgs e)
|
|
|
|
|
{
|
|
|
|
|
Clips.ToList().ForEach((fcp) => fcp.AddBlock(e.DataBlock));
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-12 18:57:20 +08:00
|
|
|
|
|
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
}
|