1/*
2 * Copyright 2016, Dario Casalinuovo
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "HTTPMediaIO.h"
8
9#include <Handler.h>
10#include <HttpRequest.h>
11#include <UrlProtocolRoster.h>
12
13#include "MediaDebug.h"
14
15
16// 10 seconds timeout
17#define HTTP_TIMEOUT 10000000
18
19using namespace BPrivate::Network;
20
21
22class FileListener : public BUrlProtocolListener, public BDataIO {
23public:
24		FileListener(HTTPMediaIO* owner)
25			:
26			BUrlProtocolListener(),
27			fRequest(NULL),
28			fAdapterIO(owner),
29			fInitSem(-1),
30			fRunning(false)
31		{
32			fInputAdapter = fAdapterIO->BuildInputAdapter();
33			fInitSem = create_sem(0, "http_streamer init sem");
34		}
35
36		virtual ~FileListener()
37		{
38			_ReleaseInit();
39		}
40
41		bool ConnectionSuccessful() const
42		{
43			return fRequest != NULL;
44		}
45
46		void ConnectionOpened(BUrlRequest* request)
47		{
48			fRequest = request;
49			fRunning = true;
50		}
51
52		void HeadersReceived(BUrlRequest* request)
53		{
54			if (request != fRequest) {
55				delete request;
56				return;
57			}
58
59			fAdapterIO->UpdateSize();
60		}
61
62		void RequestCompleted(BUrlRequest* request, bool success)
63		{
64			_ReleaseInit();
65
66			if (request != fRequest)
67				return;
68
69			fRequest = NULL;
70		}
71
72		ssize_t Write(const void* data, size_t size)
73		{
74			_ReleaseInit();
75
76			return fInputAdapter->Write(data, size);
77		}
78
79		status_t LockOnInit(bigtime_t timeout)
80		{
81			return acquire_sem_etc(fInitSem, 1, B_RELATIVE_TIMEOUT, timeout);
82		}
83
84		bool IsRunning()
85		{
86			return fRunning;
87		}
88
89private:
90		void _ReleaseInit()
91		{
92			if (fInitSem != -1) {
93				release_sem(fInitSem);
94				delete_sem(fInitSem);
95				fInitSem = -1;
96			}
97		}
98
99		BUrlRequest*	fRequest;
100		HTTPMediaIO*	fAdapterIO;
101		BInputAdapter*	fInputAdapter;
102		sem_id			fInitSem;
103		bool			fRunning;
104};
105
106
107HTTPMediaIO::HTTPMediaIO(BUrl url)
108	:
109	BAdapterIO(B_MEDIA_STREAMING | B_MEDIA_SEEKABLE, HTTP_TIMEOUT),
110	fReq(NULL),
111	fListener(NULL),
112	fReqThread(-1),
113	fUrl(url),
114	fIsMutable(false)
115{
116	CALLED();
117}
118
119
120HTTPMediaIO::~HTTPMediaIO()
121{
122	CALLED();
123
124	if (fReq != NULL) {
125		fReq->Stop();
126		status_t status;
127		wait_for_thread(fReqThread, &status);
128
129		delete fReq;
130	}
131}
132
133
134void
135HTTPMediaIO::GetFlags(int32* flags) const
136{
137	*flags = B_MEDIA_STREAMING | B_MEDIA_SEEK_BACKWARD;
138	if (fIsMutable)
139		*flags = *flags | B_MEDIA_MUTABLE_SIZE;
140}
141
142
143ssize_t
144HTTPMediaIO::WriteAt(off_t position, const void* buffer, size_t size)
145{
146	return B_NOT_SUPPORTED;
147}
148
149
150status_t
151HTTPMediaIO::SetSize(off_t size)
152{
153	return B_NOT_SUPPORTED;
154}
155
156
157status_t
158HTTPMediaIO::Open()
159{
160	CALLED();
161
162	fListener = new FileListener(this);
163
164	fReq = BUrlProtocolRoster::MakeRequest(fUrl, fListener, fListener);
165
166	if (fReq == NULL)
167		return B_ERROR;
168
169	BHttpRequest* httpReq = dynamic_cast<BHttpRequest*>(fReq);
170	if (httpReq != NULL)
171		httpReq->SetStopOnError(true);
172
173	fReqThread = fReq->Run();
174	if (fReqThread < 0)
175		return B_ERROR;
176
177	status_t ret = fListener->LockOnInit(HTTP_TIMEOUT);
178	if (ret != B_OK)
179		return ret;
180
181	return BAdapterIO::Open();
182}
183
184
185bool
186HTTPMediaIO::IsRunning() const
187{
188	if (fListener != NULL)
189		return fListener->IsRunning();
190	else if (fReq != NULL)
191		return fReq->IsRunning();
192
193	return false;
194}
195
196
197status_t
198HTTPMediaIO::SeekRequested(off_t position)
199{
200	return BAdapterIO::SeekRequested(position);
201}
202
203
204void
205HTTPMediaIO::UpdateSize()
206{
207	// At this point we decide if our size is fixed or mutable,
208	// this will change the behavior of our parent.
209	off_t size = fReq->Result().Length();
210	if (size > 0)
211		BAdapterIO::SetSize(size);
212	else
213		fIsMutable = true;
214}
215