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 16 // 10 seconds timeout 17 #define HTTP_TIMEOUT 10000000 18 19 using namespace BPrivate::Network; 20 21 22 class FileListener : public BUrlProtocolListener, public BDataIO { 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) 53 { 54 if (request != fRequest) { 55 delete request; 56 return; 57 } 58 59 fAdapterIO->UpdateSize(); 60 } 61 62 void RequestCompleted(BUrlRequest* request, bool success) 63 { 64 _ReleaseInit(); 65 66 if (request != fRequest) 67 return; 68 69 fRequest = NULL; 70 } 71 72 ssize_t Write(const void* data, size_t size) 73 { 74 _ReleaseInit(); 75 76 return fInputAdapter->Write(data, size); 77 } 78 79 status_t LockOnInit(bigtime_t timeout) 80 { 81 return acquire_sem_etc(fInitSem, 1, B_RELATIVE_TIMEOUT, timeout); 82 } 83 84 bool IsRunning() 85 { 86 return fRunning; 87 } 88 89 private: 90 void _ReleaseInit() 91 { 92 if (fInitSem != -1) { 93 release_sem(fInitSem); 94 delete_sem(fInitSem); 95 fInitSem = -1; 96 } 97 } 98 99 BUrlRequest* fRequest; 100 HTTPMediaIO* fAdapterIO; 101 BInputAdapter* fInputAdapter; 102 sem_id fInitSem; 103 bool fRunning; 104 }; 105 106 107 HTTPMediaIO::HTTPMediaIO(BUrl url) 108 : 109 BAdapterIO(B_MEDIA_STREAMING | B_MEDIA_SEEKABLE, HTTP_TIMEOUT), 110 fReq(NULL), 111 fListener(NULL), 112 fReqThread(-1), 113 fUrl(url), 114 fIsMutable(false) 115 { 116 CALLED(); 117 } 118 119 120 HTTPMediaIO::~HTTPMediaIO() 121 { 122 CALLED(); 123 124 if (fReq != NULL) { 125 fReq->Stop(); 126 status_t status; 127 wait_for_thread(fReqThread, &status); 128 129 delete fReq; 130 } 131 } 132 133 134 void 135 HTTPMediaIO::GetFlags(int32* flags) const 136 { 137 *flags = B_MEDIA_STREAMING | B_MEDIA_SEEK_BACKWARD; 138 if (fIsMutable) 139 *flags = *flags | B_MEDIA_MUTABLE_SIZE; 140 } 141 142 143 ssize_t 144 HTTPMediaIO::WriteAt(off_t position, const void* buffer, size_t size) 145 { 146 return B_NOT_SUPPORTED; 147 } 148 149 150 status_t 151 HTTPMediaIO::SetSize(off_t size) 152 { 153 return B_NOT_SUPPORTED; 154 } 155 156 157 status_t 158 HTTPMediaIO::Open() 159 { 160 CALLED(); 161 162 fListener = new FileListener(this); 163 164 fReq = BUrlProtocolRoster::MakeRequest(fUrl, fListener, fListener); 165 166 if (fReq == NULL) 167 return B_ERROR; 168 169 BHttpRequest* httpReq = dynamic_cast<BHttpRequest*>(fReq); 170 if (httpReq != NULL) 171 httpReq->SetStopOnError(true); 172 173 fReqThread = fReq->Run(); 174 if (fReqThread < 0) 175 return B_ERROR; 176 177 status_t ret = fListener->LockOnInit(HTTP_TIMEOUT); 178 if (ret != B_OK) 179 return ret; 180 181 return BAdapterIO::Open(); 182 } 183 184 185 bool 186 HTTPMediaIO::IsRunning() const 187 { 188 if (fListener != NULL) 189 return fListener->IsRunning(); 190 else if (fReq != NULL) 191 return fReq->IsRunning(); 192 193 return false; 194 } 195 196 197 status_t 198 HTTPMediaIO::SeekRequested(off_t position) 199 { 200 return BAdapterIO::SeekRequested(position); 201 } 202 203 204 void 205 HTTPMediaIO::UpdateSize() 206 { 207 // At this point we decide if our size is fixed or mutable, 208 // this will change the behavior of our parent. 209 off_t size = fReq->Result().Length(); 210 if (size > 0) 211 BAdapterIO::SetSize(size); 212 else 213 fIsMutable = true; 214 } 215