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) 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 { 137 fReq->Stop(); 138 status_t status; 139 wait_for_thread(fReqThread, &status); 140 141 delete fReq; 142 } 143 } 144 145 146 void 147 HTTPMediaIO::GetFlags(int32* flags) const 148 { 149 *flags = B_MEDIA_STREAMING | B_MEDIA_SEEK_BACKWARD; 150 if (fIsMutable) 151 *flags = *flags | B_MEDIA_MUTABLE_SIZE; 152 } 153 154 155 ssize_t 156 HTTPMediaIO::WriteAt(off_t position, const void* buffer, size_t size) 157 { 158 return B_NOT_SUPPORTED; 159 } 160 161 162 status_t 163 HTTPMediaIO::SetSize(off_t size) 164 { 165 return B_NOT_SUPPORTED; 166 } 167 168 169 status_t 170 HTTPMediaIO::Open() 171 { 172 CALLED(); 173 174 fListener = new FileListener(this); 175 176 fReq = BUrlProtocolRoster::MakeRequest(fUrl, fListener); 177 178 if (fReq == NULL) 179 return B_ERROR; 180 181 fReqThread = fReq->Run(); 182 if (fReqThread < 0) 183 return B_ERROR; 184 185 status_t ret = fListener->LockOnInit(HTTP_TIMEOUT); 186 if (ret != B_OK) 187 return ret; 188 189 return BAdapterIO::Open(); 190 } 191 192 193 bool 194 HTTPMediaIO::IsRunning() const 195 { 196 if (fListener != NULL) 197 return fListener->IsRunning(); 198 else if (fReq != NULL) 199 return fReq->IsRunning(); 200 201 return false; 202 } 203 204 205 status_t 206 HTTPMediaIO::SeekRequested(off_t position) 207 { 208 return BAdapterIO::SeekRequested(position); 209 } 210 211 212 void 213 HTTPMediaIO::UpdateSize() 214 { 215 // At this point we decide if our size is fixed or mutable, 216 // this will change the behavior of our parent. 217 off_t size = fReq->Result().Length(); 218 if (size > 0) 219 BAdapterIO::SetSize(size); 220 else 221 fIsMutable = true; 222 } 223