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