xref: /haiku/src/add-ons/media/plugins/http_streamer/HTTPMediaIO.cpp (revision 4d8811742fa447ec05b4993a16a0931bc29aafab)
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, const BUrlResult& result)
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 		fReq->Stop();
137 		status_t status;
138 		wait_for_thread(fReqThread, &status);
139 
140 		delete fReq;
141 	}
142 }
143 
144 
145 void
146 HTTPMediaIO::GetFlags(int32* flags) const
147 {
148 	*flags = B_MEDIA_STREAMING | B_MEDIA_SEEK_BACKWARD;
149 	if (fIsMutable)
150 		*flags = *flags | B_MEDIA_MUTABLE_SIZE;
151 }
152 
153 
154 ssize_t
155 HTTPMediaIO::WriteAt(off_t position, const void* buffer, size_t size)
156 {
157 	return B_NOT_SUPPORTED;
158 }
159 
160 
161 status_t
162 HTTPMediaIO::SetSize(off_t size)
163 {
164 	return B_NOT_SUPPORTED;
165 }
166 
167 
168 status_t
169 HTTPMediaIO::Open()
170 {
171 	CALLED();
172 
173 	fListener = new FileListener(this);
174 
175 	fReq = BUrlProtocolRoster::MakeRequest(fUrl, fListener);
176 
177 	if (fReq == NULL)
178 		return B_ERROR;
179 
180 	fReqThread = fReq->Run();
181 	if (fReqThread < 0)
182 		return B_ERROR;
183 
184 	status_t ret = fListener->LockOnInit(HTTP_TIMEOUT);
185 	if (ret != B_OK)
186 		return ret;
187 
188 	return BAdapterIO::Open();
189 }
190 
191 
192 bool
193 HTTPMediaIO::IsRunning() const
194 {
195 	if (fListener != NULL)
196 		return fListener->IsRunning();
197 	else if (fReq != NULL)
198 		return fReq->IsRunning();
199 
200 	return false;
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