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