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