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