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, sizeof(fStreamInfo[i].encodedFormat)); 46 } 47 48 // create all stream cookies 49 for (int32 i = 0; i < fStreamCount; i++) { 50 if (B_OK != fReader->AllocateCookie(i, &fStreamInfo[i].cookie)) { 51 fStreamInfo[i].cookie = 0; 52 fStreamInfo[i].hasCookie = false; 53 fStreamInfo[i].status = B_ERROR; 54 printf("MediaExtractor::MediaExtractor: AllocateCookie for stream %ld failed\n", i); 55 } 56 } 57 58 // get info for all streams 59 for (int32 i = 0; i < fStreamCount; i++) { 60 if (fStreamInfo[i].status != B_OK) 61 continue; 62 int64 frameCount; 63 bigtime_t duration; 64 if (B_OK != fReader->GetStreamInfo(fStreamInfo[i].cookie, &frameCount, &duration, 65 &fStreamInfo[i].encodedFormat, 66 &fStreamInfo[i].infoBuffer, 67 &fStreamInfo[i].infoBufferSize)) { 68 fStreamInfo[i].status = B_ERROR; 69 printf("MediaExtractor::MediaExtractor: GetStreamInfo for stream %ld failed\n", i); 70 } 71 } 72 73 #if DISABLE_CHUNK_CACHE == 0 74 // start extractor thread 75 fExtractorWaitSem = create_sem(1, "media extractor thread sem"); 76 fExtractorThread = spawn_thread(extractor_thread, "media extractor thread", 10, this); 77 resume_thread(fExtractorThread); 78 #endif 79 } 80 81 82 MediaExtractor::~MediaExtractor() 83 { 84 CALLED(); 85 86 // terminate extractor thread 87 fTerminateExtractor = true; 88 release_sem(fExtractorWaitSem); 89 status_t err; 90 wait_for_thread(fExtractorThread, &err); 91 delete_sem(fExtractorWaitSem); 92 93 // free all stream cookies 94 // and chunk caches 95 for (int32 i = 0; i < fStreamCount; i++) { 96 if (fStreamInfo[i].hasCookie) 97 fReader->FreeCookie(fStreamInfo[i].cookie); 98 delete fStreamInfo[i].chunkCache; 99 } 100 101 _plugin_manager.DestroyReader(fReader); 102 103 delete [] fStreamInfo; 104 // fSource is owned by the BMediaFile 105 } 106 107 108 status_t 109 MediaExtractor::InitCheck() 110 { 111 CALLED(); 112 return fErr; 113 } 114 115 116 void 117 MediaExtractor::GetFileFormatInfo(media_file_format *mfi) const 118 { 119 CALLED(); 120 *mfi = fMff; 121 } 122 123 124 int32 125 MediaExtractor::StreamCount() 126 { 127 CALLED(); 128 return fStreamCount; 129 } 130 131 132 const media_format * 133 MediaExtractor::EncodedFormat(int32 stream) 134 { 135 return &fStreamInfo[stream].encodedFormat; 136 } 137 138 139 int64 140 MediaExtractor::CountFrames(int32 stream) const 141 { 142 CALLED(); 143 int64 frameCount; 144 bigtime_t duration; 145 media_format format; 146 const void *infoBuffer; 147 size_t infoSize; 148 149 fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration, &format, &infoBuffer, &infoSize); 150 151 return frameCount; 152 } 153 154 155 bigtime_t 156 MediaExtractor::Duration(int32 stream) const 157 { 158 CALLED(); 159 int64 frameCount; 160 bigtime_t duration; 161 media_format format; 162 const void *infoBuffer; 163 size_t infoSize; 164 165 fReader->GetStreamInfo(fStreamInfo[stream].cookie, &frameCount, &duration, &format, &infoBuffer, &infoSize); 166 167 return duration; 168 } 169 170 171 status_t 172 MediaExtractor::Seek(int32 stream, uint32 seekTo, 173 int64 *frame, bigtime_t *time) 174 { 175 CALLED(); 176 if (fStreamInfo[stream].status != B_OK) 177 return fStreamInfo[stream].status; 178 179 status_t result; 180 result = fReader->Seek(fStreamInfo[stream].cookie, seekTo, frame, time); 181 if (result != B_OK) 182 return result; 183 184 // clear buffered chunks 185 fStreamInfo[stream].chunkCache->MakeEmpty(); 186 release_sem(fExtractorWaitSem); 187 188 return B_OK; 189 } 190 191 192 status_t 193 MediaExtractor::GetNextChunk(int32 stream, 194 const void **chunkBuffer, size_t *chunkSize, 195 media_header *mediaHeader) 196 { 197 if (fStreamInfo[stream].status != B_OK) 198 return fStreamInfo[stream].status; 199 200 #if DISABLE_CHUNK_CACHE > 0 201 static BLocker locker; 202 BAutolock lock(locker); 203 return fReader->GetNextChunk(fStreamInfo[stream].cookie, chunkBuffer, chunkSize, mediaHeader); 204 #endif 205 206 status_t err; 207 err = fStreamInfo[stream].chunkCache->GetNextChunk(chunkBuffer, chunkSize, mediaHeader); 208 release_sem(fExtractorWaitSem); 209 return err; 210 } 211 212 213 class MediaExtractorChunkProvider : public ChunkProvider 214 { 215 private: 216 MediaExtractor * fExtractor; 217 int32 fStream; 218 public: 219 MediaExtractorChunkProvider(MediaExtractor * extractor, int32 stream) 220 { 221 fExtractor = extractor; 222 fStream = stream; 223 } 224 225 virtual status_t GetNextChunk(const void **chunkBuffer, size_t *chunkSize, 226 media_header *mediaHeader) 227 { 228 return fExtractor->GetNextChunk(fStream, chunkBuffer, chunkSize, mediaHeader); 229 } 230 }; 231 232 233 status_t 234 MediaExtractor::CreateDecoder(int32 stream, Decoder **out_decoder, media_codec_info *mci) 235 { 236 CALLED(); 237 status_t res; 238 Decoder *decoder; 239 240 res = fStreamInfo[stream].status; 241 if (res != B_OK) { 242 printf("MediaExtractor::CreateDecoder can't create decoder for stream %ld\n", stream); 243 return res; 244 } 245 246 res = _plugin_manager.CreateDecoder(&decoder, fStreamInfo[stream].encodedFormat); 247 if (res != B_OK) { 248 printf("MediaExtractor::CreateDecoder _plugin_manager.CreateDecoder failed for stream %ld\n", stream); 249 return res; 250 } 251 252 ChunkProvider *chunkProvider = new(std::nothrow) MediaExtractorChunkProvider(this, stream); 253 if (!chunkProvider) { 254 _plugin_manager.DestroyDecoder(decoder); 255 printf("MediaExtractor::CreateDecoder can't create chunk provider for stream %ld\n", stream); 256 return B_NO_MEMORY; 257 } 258 259 decoder->SetChunkProvider(chunkProvider); 260 261 res = decoder->Setup(&fStreamInfo[stream].encodedFormat, fStreamInfo[stream].infoBuffer, fStreamInfo[stream].infoBufferSize); 262 if (res != B_OK) { 263 _plugin_manager.DestroyDecoder(decoder); 264 printf("MediaExtractor::CreateDecoder Setup failed for stream %ld: %ld (%s)\n", stream, res, strerror(res)); 265 return res; 266 } 267 268 res = _plugin_manager.GetDecoderInfo(decoder, mci); 269 if (res != B_OK) { 270 _plugin_manager.DestroyDecoder(decoder); 271 printf("MediaExtractor::CreateDecoder GetCodecInfo failed for stream %ld: %ld (%s)\n", stream, res, strerror(res)); 272 return res; 273 } 274 275 *out_decoder = decoder; 276 return B_OK; 277 } 278 279 280 int32 281 MediaExtractor::extractor_thread(void *arg) 282 { 283 static_cast<MediaExtractor *>(arg)->ExtractorThread(); 284 return 0; 285 } 286 287 288 void 289 MediaExtractor::ExtractorThread() 290 { 291 for (;;) { 292 acquire_sem(fExtractorWaitSem); 293 if (fTerminateExtractor) 294 return; 295 296 bool refill_done; 297 do { 298 refill_done = false; 299 for (int32 stream = 0; stream < fStreamCount; stream++) { 300 if (fStreamInfo[stream].status != B_OK) 301 continue; 302 if (fStreamInfo[stream].chunkCache->NeedsRefill()) { 303 media_header mediaHeader; 304 const void *chunkBuffer; 305 size_t chunkSize; 306 status_t err; 307 err = fReader->GetNextChunk(fStreamInfo[stream].cookie, &chunkBuffer, &chunkSize, &mediaHeader); 308 fStreamInfo[stream].chunkCache->PutNextChunk(chunkBuffer, chunkSize, mediaHeader, err); 309 refill_done = true; 310 } 311 } 312 if (fTerminateExtractor) 313 return; 314 } while (refill_done); 315 } 316 } 317