1 /* 2 ** Copyright 2004-2007, Marcus Overhagen. All rights reserved. 3 ** Copyright 2008, Maurice Kalinowski. All rights reserved. 4 ** Distributed under the terms of the MIT License. 5 */ 6 #include "MediaExtractor.h" 7 #include "PluginManager.h" 8 #include "ChunkCache.h" 9 #include "debug.h" 10 11 #include <Autolock.h> 12 13 #include <stdio.h> 14 #include <string.h> 15 #include <new> 16 17 // should be 0, to disable the chunk cache set it to 1 18 #define DISABLE_CHUNK_CACHE 0 19 20 MediaExtractor::MediaExtractor(BDataIO *source, int32 flags) 21 { 22 CALLED(); 23 fSource = source; 24 fStreamInfo = NULL; 25 fExtractorThread = -1; 26 fExtractorWaitSem = -1; 27 fTerminateExtractor = false; 28 29 fErr = _plugin_manager.CreateReader(&fReader, &fStreamCount, &fMff, source); 30 if (fErr) { 31 fStreamCount = 0; 32 fReader = NULL; 33 return; 34 } 35 36 fStreamInfo = new stream_info[fStreamCount]; 37 38 // initialize stream infos 39 for (int32 i = 0; i < fStreamCount; i++) { 40 fStreamInfo[i].status = B_OK; 41 fStreamInfo[i].cookie = 0; 42 fStreamInfo[i].hasCookie = true; 43 fStreamInfo[i].infoBuffer = 0; 44 fStreamInfo[i].infoBufferSize = 0; 45 fStreamInfo[i].chunkCache = new ChunkCache; 46 memset(&fStreamInfo[i].encodedFormat, 0, 47 sizeof(fStreamInfo[i].encodedFormat)); 48 } 49 50 // create all stream cookies 51 for (int32 i = 0; i < fStreamCount; i++) { 52 if (B_OK != fReader->AllocateCookie(i, &fStreamInfo[i].cookie)) { 53 fStreamInfo[i].cookie = 0; 54 fStreamInfo[i].hasCookie = false; 55 fStreamInfo[i].status = B_ERROR; 56 ERROR("MediaExtractor::MediaExtractor: AllocateCookie for stream " 57 "%ld failed\n", i); 58 } 59 } 60 61 // get info for all streams 62 for (int32 i = 0; i < fStreamCount; i++) { 63 if (fStreamInfo[i].status != B_OK) 64 continue; 65 int64 frameCount; 66 bigtime_t duration; 67 if (B_OK != fReader->GetStreamInfo(fStreamInfo[i].cookie, &frameCount, 68 &duration, &fStreamInfo[i].encodedFormat, 69 &fStreamInfo[i].infoBuffer, &fStreamInfo[i].infoBufferSize)) { 70 fStreamInfo[i].status = B_ERROR; 71 ERROR("MediaExtractor::MediaExtractor: GetStreamInfo for " 72 "stream %ld failed\n", i); 73 } 74 } 75 76 #if DISABLE_CHUNK_CACHE == 0 77 // start extractor thread 78 fExtractorWaitSem = create_sem(1, "media extractor thread sem"); 79 fExtractorThread = spawn_thread(extractor_thread, "media extractor thread", 80 10, this); 81 resume_thread(fExtractorThread); 82 #endif 83 } 84 85 86 MediaExtractor::~MediaExtractor() 87 { 88 CALLED(); 89 90 // terminate extractor thread 91 fTerminateExtractor = true; 92 release_sem(fExtractorWaitSem); 93 status_t err; 94 wait_for_thread(fExtractorThread, &err); 95 delete_sem(fExtractorWaitSem); 96 97 // free all stream cookies 98 // and chunk caches 99 for (int32 i = 0; i < fStreamCount; i++) { 100 if (fStreamInfo[i].hasCookie) 101 fReader->FreeCookie(fStreamInfo[i].cookie); 102 delete fStreamInfo[i].chunkCache; 103 } 104 105 _plugin_manager.DestroyReader(fReader); 106 107 delete [] fStreamInfo; 108 // fSource is owned by the BMediaFile 109 } 110 111 112 status_t 113 MediaExtractor::InitCheck() 114 { 115 CALLED(); 116 return fErr; 117 } 118 119 120 void 121 MediaExtractor::GetFileFormatInfo(media_file_format *mfi) const 122 { 123 CALLED(); 124 *mfi = fMff; 125 } 126 127 128 int32 129 MediaExtractor::StreamCount() 130 { 131 CALLED(); 132 return fStreamCount; 133 } 134 135 136 const char* 137 MediaExtractor::Copyright() 138 { 139 return fReader->Copyright(); 140 } 141 142 143 const media_format * 144 MediaExtractor::EncodedFormat(int32 stream) 145 { 146 return &fStreamInfo[stream].encodedFormat; 147 } 148 149 150 int64 151 MediaExtractor::CountFrames(int32 stream) const 152 { 153 CALLED(); 154 if (fStreamInfo[stream].status != B_OK) 155 return 0LL; 156 157 int64 frameCount; 158 bigtime_t duration; 159 media_format format; 160 const void *infoBuffer; 161 size_t infoSize; 162 163 fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration, 164 &format, &infoBuffer, &infoSize); 165 166 return frameCount; 167 } 168 169 170 bigtime_t 171 MediaExtractor::Duration(int32 stream) const 172 { 173 CALLED(); 174 175 if (fStreamInfo[stream].status != B_OK) 176 return 0LL; 177 178 int64 frameCount; 179 bigtime_t duration; 180 media_format format; 181 const void *infoBuffer; 182 size_t infoSize; 183 184 fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration, 185 &format, &infoBuffer, &infoSize); 186 187 return duration; 188 } 189 190 191 status_t 192 MediaExtractor::Seek(int32 stream, uint32 seekTo, 193 int64 *frame, bigtime_t *time) 194 { 195 CALLED(); 196 if (fStreamInfo[stream].status != B_OK) 197 return fStreamInfo[stream].status; 198 199 status_t result; 200 result = fReader->Seek(fStreamInfo[stream].cookie, seekTo, frame, time); 201 if (result != B_OK) 202 return result; 203 204 // clear buffered chunks 205 fStreamInfo[stream].chunkCache->MakeEmpty(); 206 release_sem(fExtractorWaitSem); 207 208 return B_OK; 209 } 210 211 212 status_t 213 MediaExtractor::FindKeyFrame(int32 stream, uint32 seekTo, int64 *frame, 214 bigtime_t *time) const 215 { 216 CALLED(); 217 if (fStreamInfo[stream].status != B_OK) 218 return fStreamInfo[stream].status; 219 220 return fReader->FindKeyFrame(fStreamInfo[stream].cookie, 221 seekTo, frame, time); 222 } 223 224 225 status_t 226 MediaExtractor::GetNextChunk(int32 stream, 227 const void **chunkBuffer, size_t *chunkSize, 228 media_header *mediaHeader) 229 { 230 if (fStreamInfo[stream].status != B_OK) 231 return fStreamInfo[stream].status; 232 233 #if DISABLE_CHUNK_CACHE > 0 234 static BLocker locker; 235 BAutolock lock(locker); 236 return fReader->GetNextChunk(fStreamInfo[stream].cookie, chunkBuffer, 237 chunkSize, mediaHeader); 238 #endif 239 240 status_t err; 241 err = fStreamInfo[stream].chunkCache->GetNextChunk(chunkBuffer, chunkSize, 242 mediaHeader); 243 release_sem(fExtractorWaitSem); 244 return err; 245 } 246 247 248 class MediaExtractorChunkProvider : public ChunkProvider { 249 private: 250 MediaExtractor * fExtractor; 251 int32 fStream; 252 public: 253 MediaExtractorChunkProvider(MediaExtractor * extractor, int32 stream) 254 { 255 fExtractor = extractor; 256 fStream = stream; 257 } 258 259 virtual status_t GetNextChunk(const void **chunkBuffer, size_t *chunkSize, 260 media_header *mediaHeader) 261 { 262 return fExtractor->GetNextChunk(fStream, chunkBuffer, chunkSize, 263 mediaHeader); 264 } 265 }; 266 267 268 status_t 269 MediaExtractor::CreateDecoder(int32 stream, Decoder **out_decoder, 270 media_codec_info *mci) 271 { 272 CALLED(); 273 status_t res; 274 Decoder *decoder; 275 276 res = fStreamInfo[stream].status; 277 if (res != B_OK) { 278 ERROR("MediaExtractor::CreateDecoder can't create decoder for " 279 "stream %ld\n", stream); 280 return res; 281 } 282 283 res = _plugin_manager.CreateDecoder(&decoder, 284 fStreamInfo[stream].encodedFormat); 285 if (res != B_OK) { 286 char formatString[256]; 287 string_for_format(fStreamInfo[stream].encodedFormat, formatString, 288 256); 289 ERROR("MediaExtractor::CreateDecoder _plugin_manager.CreateDecoder " 290 "failed for stream %ld, format: %s\n", stream, formatString); 291 return res; 292 } 293 294 ChunkProvider *chunkProvider 295 = new(std::nothrow) MediaExtractorChunkProvider(this, stream); 296 if (!chunkProvider) { 297 _plugin_manager.DestroyDecoder(decoder); 298 ERROR("MediaExtractor::CreateDecoder can't create chunk provider " 299 "for stream %ld\n", stream); 300 return B_NO_MEMORY; 301 } 302 303 decoder->SetChunkProvider(chunkProvider); 304 305 res = decoder->Setup(&fStreamInfo[stream].encodedFormat, 306 fStreamInfo[stream].infoBuffer, fStreamInfo[stream].infoBufferSize); 307 if (res != B_OK) { 308 _plugin_manager.DestroyDecoder(decoder); 309 ERROR("MediaExtractor::CreateDecoder Setup failed for stream %ld: " 310 "%ld (%s)\n", stream, res, strerror(res)); 311 return res; 312 } 313 314 res = _plugin_manager.GetDecoderInfo(decoder, mci); 315 if (res != B_OK) { 316 _plugin_manager.DestroyDecoder(decoder); 317 ERROR("MediaExtractor::CreateDecoder GetCodecInfo failed for stream " 318 "%ld: %ld (%s)\n", stream, res, strerror(res)); 319 return res; 320 } 321 322 *out_decoder = decoder; 323 return B_OK; 324 } 325 326 327 int32 328 MediaExtractor::extractor_thread(void *arg) 329 { 330 static_cast<MediaExtractor *>(arg)->ExtractorThread(); 331 return 0; 332 } 333 334 335 void 336 MediaExtractor::ExtractorThread() 337 { 338 for (;;) { 339 acquire_sem(fExtractorWaitSem); 340 if (fTerminateExtractor) 341 return; 342 343 bool refill_done; 344 do { 345 refill_done = false; 346 for (int32 stream = 0; stream < fStreamCount; stream++) { 347 if (fStreamInfo[stream].status != B_OK) 348 continue; 349 if (fStreamInfo[stream].chunkCache->NeedsRefill()) { 350 media_header mediaHeader; 351 const void *chunkBuffer; 352 size_t chunkSize; 353 status_t err; 354 err = fReader->GetNextChunk(fStreamInfo[stream].cookie, 355 &chunkBuffer, &chunkSize, &mediaHeader); 356 fStreamInfo[stream].chunkCache->PutNextChunk(chunkBuffer, 357 chunkSize, mediaHeader, err); 358 refill_done = true; 359 } 360 } 361 if (fTerminateExtractor) 362 return; 363 } while (refill_done); 364 } 365 } 366