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