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