xref: /haiku/src/add-ons/media/plugins/http_streamer/HTTPMediaIO.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 using namespace BCodecKit;
16 
17 
18 // 10 seconds timeout
19 #define HTTP_TIMEOUT 10000000
20 
21 
22 class FileListener : public BUrlProtocolListener {
23 public:
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, const BUrlResult& result)
53 		{
54 			fAdapterIO->UpdateSize();
55 		}
56 
57 		void DataReceived(BUrlRequest* request, const char* data,
58 			off_t position, ssize_t size)
59 		{
60 			if (request != fRequest) {
61 				delete request;
62 				return;
63 			}
64 
65 			BHttpRequest* httpReq = dynamic_cast<BHttpRequest*>(request);
66 			if (httpReq != NULL) {
67 				const BHttpResult& httpRes
68 					= (const BHttpResult&)httpReq->Result();
69 				int32 status = httpRes.StatusCode();
70 				if (BHttpRequest::IsClientErrorStatusCode(status)
71 						|| BHttpRequest::IsServerErrorStatusCode(status)) {
72 					fRunning = false;
73 				} else if (BHttpRequest::IsRedirectionStatusCode(status))
74 					return;
75 			}
76 
77 			_ReleaseInit();
78 
79 			fInputAdapter->Write(data, size);
80 		}
81 
82 		void RequestCompleted(BUrlRequest* request, bool success)
83 		{
84 			_ReleaseInit();
85 
86 			if (request != fRequest)
87 				return;
88 
89 			fRequest = NULL;
90 		}
91 
92 		status_t LockOnInit(bigtime_t timeout)
93 		{
94 			return acquire_sem_etc(fInitSem, 1, B_RELATIVE_TIMEOUT, timeout);
95 		}
96 
97 		bool IsRunning()
98 		{
99 			return fRunning;
100 		}
101 
102 private:
103 		void _ReleaseInit()
104 		{
105 			if (fInitSem != -1) {
106 				release_sem(fInitSem);
107 				delete_sem(fInitSem);
108 				fInitSem = -1;
109 			}
110 		}
111 
112 		BUrlRequest*	fRequest;
113 		HTTPMediaIO*	fAdapterIO;
114 		BInputAdapter*	fInputAdapter;
115 		sem_id			fInitSem;
116 		bool			fRunning;
117 };
118 
119 
120 HTTPMediaIO::HTTPMediaIO(BUrl url)
121 	:
122 	BAdapterIO(B_MEDIA_STREAMING | B_MEDIA_SEEKABLE, HTTP_TIMEOUT),
123 	fReq(NULL),
124 	fListener(NULL),
125 	fReqThread(-1),
126 	fUrl(url),
127 	fIsMutable(false)
128 {
129 	CALLED();
130 }
131 
132 
133 HTTPMediaIO::~HTTPMediaIO()
134 {
135 	CALLED();
136 
137 	if (fReq != NULL) {
138 		fReq->Stop();
139 		status_t status;
140 		wait_for_thread(fReqThread, &status);
141 
142 		delete fReq;
143 	}
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, fListener);
178 
179 	if (fReq == NULL)
180 		return B_ERROR;
181 
182 	fReqThread = fReq->Run();
183 	if (fReqThread < 0)
184 		return B_ERROR;
185 
186 	status_t ret = fListener->LockOnInit(HTTP_TIMEOUT);
187 	if (ret != B_OK)
188 		return ret;
189 
190 	return BAdapterIO::Open();
191 }
192 
193 
194 bool
195 HTTPMediaIO::IsRunning() const
196 {
197 	if (fListener != NULL)
198 		return fListener->IsRunning();
199 	else if (fReq != NULL)
200 		return fReq->IsRunning();
201 
202 	return false;
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