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