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