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