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