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