xref: /haiku/src/kits/media/MediaExtractor.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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 	// TODO Here we should work out a way so that if there is a setup failure we can try the next decoder
284 	res = _plugin_manager.CreateDecoder(&decoder,
285 		fStreamInfo[stream].encodedFormat);
286 	if (res != B_OK) {
287 		char formatString[256];
288 		string_for_format(fStreamInfo[stream].encodedFormat, formatString,
289 			256);
290 		ERROR("MediaExtractor::CreateDecoder _plugin_manager.CreateDecoder "
291 			"failed for stream %ld, format: %s\n", stream, formatString);
292 		return res;
293 	}
294 
295 	ChunkProvider *chunkProvider
296 		= new(std::nothrow) MediaExtractorChunkProvider(this, stream);
297 	if (!chunkProvider) {
298 		_plugin_manager.DestroyDecoder(decoder);
299 		ERROR("MediaExtractor::CreateDecoder can't create chunk provider "
300 			"for stream %ld\n", stream);
301 		return B_NO_MEMORY;
302 	}
303 
304 	decoder->SetChunkProvider(chunkProvider);
305 
306 	res = decoder->Setup(&fStreamInfo[stream].encodedFormat,
307 		fStreamInfo[stream].infoBuffer, fStreamInfo[stream].infoBufferSize);
308 	if (res != B_OK) {
309 		_plugin_manager.DestroyDecoder(decoder);
310 		ERROR("MediaExtractor::CreateDecoder Setup failed for stream %ld: "
311 			"%ld (%s)\n", stream, res, strerror(res));
312 		return res;
313 	}
314 
315 	res = _plugin_manager.GetDecoderInfo(decoder, mci);
316 	if (res != B_OK) {
317 		_plugin_manager.DestroyDecoder(decoder);
318 		ERROR("MediaExtractor::CreateDecoder GetCodecInfo failed for stream "
319 			"%ld: %ld (%s)\n", stream, res, strerror(res));
320 		return res;
321 	}
322 
323 	*out_decoder = decoder;
324 	return B_OK;
325 }
326 
327 
328 int32
329 MediaExtractor::extractor_thread(void *arg)
330 {
331 	static_cast<MediaExtractor *>(arg)->ExtractorThread();
332 	return 0;
333 }
334 
335 
336 void
337 MediaExtractor::ExtractorThread()
338 {
339 	for (;;) {
340 		acquire_sem(fExtractorWaitSem);
341 		if (fTerminateExtractor)
342 			return;
343 
344 		bool refill_done;
345 		do {
346 			refill_done = false;
347 			for (int32 stream = 0; stream < fStreamCount; stream++) {
348 				if (fStreamInfo[stream].status != B_OK)
349 					continue;
350 				if (fStreamInfo[stream].chunkCache->NeedsRefill()) {
351 					media_header mediaHeader;
352 					const void *chunkBuffer;
353 					size_t chunkSize;
354 					status_t err;
355 					err = fReader->GetNextChunk(fStreamInfo[stream].cookie,
356 						&chunkBuffer, &chunkSize, &mediaHeader);
357 					fStreamInfo[stream].chunkCache->PutNextChunk(chunkBuffer,
358 						chunkSize, mediaHeader, err);
359 					refill_done = true;
360 				}
361 			}
362 			if (fTerminateExtractor)
363 				return;
364 		} while (refill_done);
365 	}
366 }
367