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