xref: /haiku/src/kits/media/MediaExtractor.cpp (revision b06a48ab8f30b45916a9c157b992827779182163)
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