1/*
2 * Copyright (c) 2005, David McPaul
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 *  * Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23 * OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26
27#include "ASFFileReader.h"
28
29#include <DataIO.h>
30#include <SupportKit.h>
31
32#include <iostream>
33
34
35ASFFileReader::ASFFileReader(BPositionIO *pStream)
36{
37	theStream = pStream;
38
39	// Find Size of Stream, need to rethink this for non seekable streams
40	theStream->Seek(0,SEEK_END);
41	StreamSize = theStream->Position();
42	theStream->Seek(0,SEEK_SET);
43	packet = asf_packet_create();
44}
45
46
47ASFFileReader::~ASFFileReader()
48{
49	if (packet) {
50		asf_packet_destroy(packet);
51		packet = NULL;
52	}
53
54	if (asfFile) {
55		asf_close(asfFile);
56		asfFile = NULL;
57	}
58
59	theStream = NULL;
60	streams.clear();
61}
62
63status_t
64ASFFileReader::ParseFile()
65{
66	asf_iostream_t ioStream;
67	ioStream.opaque = theStream;
68	ioStream.read = &ASFFileReader::read;
69	ioStream.write = &ASFFileReader::write;
70	ioStream.seek = &ASFFileReader::seek;
71
72	asfFile = asf_open_cb(&ioStream);
73
74	int result = asf_init(asfFile);
75
76	if (result != 0) {
77		printf("error initialising asf asf_init returned %d\n",result);
78		return B_ERROR;
79	}
80
81	asf_metadata_t *metadata = asf_header_get_metadata(asfFile);
82
83	if (metadata) {
84		printf("Title %s\n",metadata->title);
85		printf("Artist %s\n",metadata->artist);
86		printf("Copyright %s\n",metadata->copyright);
87		printf("Description %s\n",metadata->description);
88		printf("Rating %s\n",metadata->rating);
89		printf("Additional Entries %d\n",metadata->extended_count);
90
91		asf_metadata_destroy(metadata);
92	}
93
94	uint32 totalStreams = getStreamCount();
95	StreamEntry streamEntry;
96
97	for (uint32 i=0;i < totalStreams;i++) {
98		streamEntry.streamIndex = i;
99		streams.push_back(streamEntry);
100	}
101
102	ParseIndex();
103
104	// load the first packet
105	if (asf_get_packet(asfFile, packet) < 0) {
106		printf("Could not get first packet\n");
107		return B_ERROR;
108	}
109
110	return B_OK;
111}
112
113
114bool
115ASFFileReader::IsEndOfData(off_t pPosition)
116{
117	return true;
118}
119
120
121bool
122ASFFileReader::IsEndOfFile(off_t position)
123{
124	return (position >= StreamSize);
125}
126
127
128bool
129ASFFileReader::IsEndOfFile()
130{
131	return theStream->Position() >= StreamSize;
132}
133
134uint32
135ASFFileReader::getStreamCount()
136{
137	return asf_get_stream_count(asfFile) + 1;
138}
139
140bool
141ASFFileReader::getAudioFormat(uint32 streamIndex, ASFAudioFormat *format)
142{
143	asf_waveformatex_t *audioHeader;
144	asf_stream_t *stream;
145
146	if (IsAudio(streamIndex)) {
147		stream = asf_get_stream(asfFile, streamIndex);
148
149		if (stream) {
150			audioHeader = (asf_waveformatex_t *)(stream->properties);
151			format->Compression = audioHeader->wFormatTag;
152			format->NoChannels = audioHeader->nChannels;
153			format->SamplesPerSec = audioHeader->nSamplesPerSec;
154			format->AvgBytesPerSec = audioHeader->nAvgBytesPerSec;
155			format->BlockAlign = audioHeader->nBlockAlign;
156			format->BitsPerSample = audioHeader->wBitsPerSample;
157			format->extraDataSize = audioHeader->cbSize;
158			format->extraData = audioHeader->data;
159
160			return true;
161		}
162	}
163
164	return false;
165}
166
167bool
168ASFFileReader::getVideoFormat(uint32 streamIndex, ASFVideoFormat *format)
169{
170	asf_bitmapinfoheader_t *videoHeader;
171	asf_stream_t *stream;
172
173	if (IsVideo(streamIndex)) {
174		stream = asf_get_stream(asfFile, streamIndex);
175
176		if (stream) {
177			videoHeader = (asf_bitmapinfoheader_t *)(stream->properties);
178			format->Compression = videoHeader->biCompression;
179			format->VideoWidth = videoHeader->biWidth;
180			format->VideoHeight = videoHeader->biHeight;
181			format->Planes = videoHeader->biPlanes;
182			format->BitCount = videoHeader->biBitCount;
183			format->extraDataSize = videoHeader->biSize - 40;
184			format->extraData = videoHeader->data;
185
186			if (stream->flags & ASF_STREAM_FLAG_EXTENDED) {
187				format->FrameScale = stream->extended_properties->avg_time_per_frame;
188				format->FrameRate = 10000000L;
189				printf("num avg time per frame for video %Ld\n",stream->extended_properties->avg_time_per_frame);
190			}
191
192			return true;
193		}
194	}
195
196	return false;
197}
198
199
200bigtime_t
201ASFFileReader::getStreamDuration(uint32 streamIndex)
202{
203	if (streamIndex < streams.size()) {
204		return streams[streamIndex].getDuration();
205	}
206
207	asf_stream_t *stream;
208
209	stream = asf_get_stream(asfFile, streamIndex);
210
211	if (stream) {
212		if (stream->flags & ASF_STREAM_FLAG_EXTENDED) {
213			printf("STREAM %ld end time %Ld, start time %Ld\n",streamIndex, stream->extended_properties->end_time, stream->extended_properties->start_time);
214			if (stream->extended_properties->end_time - stream->extended_properties->start_time > 0) {
215				return stream->extended_properties->end_time - stream->extended_properties->start_time;
216			}
217		}
218	}
219
220	return asf_get_duration(asfFile) / 10L;
221}
222
223uint32
224ASFFileReader::getFrameCount(uint32 streamIndex)
225{
226	if (streamIndex < streams.size()) {
227		return streams[streamIndex].getFrameCount();
228	}
229
230	return 0;
231}
232
233bool
234ASFFileReader::IsVideo(uint32 streamIndex)
235{
236	asf_stream_t *stream;
237
238	stream = asf_get_stream(asfFile, streamIndex);
239
240	if (stream) {
241		return ((stream->type == ASF_STREAM_TYPE_VIDEO) && (stream->flags & ASF_STREAM_FLAG_AVAILABLE));
242	}
243
244	return false;
245}
246
247
248bool
249ASFFileReader::IsAudio(uint32 streamIndex)
250{
251	asf_stream_t *stream;
252
253	stream = asf_get_stream(asfFile, streamIndex);
254
255	if (stream) {
256		return ((stream->type == ASF_STREAM_TYPE_AUDIO) && (stream->flags & ASF_STREAM_FLAG_AVAILABLE));
257	}
258
259	return false;
260}
261
262IndexEntry
263ASFFileReader::GetIndex(uint32 streamIndex, uint32 frameNo)
264{
265	return streams[streamIndex].GetIndex(frameNo);
266}
267
268bool
269ASFFileReader::HasIndex(uint32 streamIndex, uint32 frameNo)
270{
271	if (streamIndex < streams.size()) {
272		return streams[streamIndex].HasIndex(frameNo);
273	}
274
275	return false;
276}
277
278uint32
279ASFFileReader::GetFrameForTime(uint32 streamIndex, bigtime_t time)
280{
281	if (streamIndex < streams.size()) {
282		return streams[streamIndex].GetIndex(time).frameNo;
283	}
284
285	return 0;
286}
287
288void
289ASFFileReader::ParseIndex() {
290	// Try to build some sort of useful index
291	// packet->send_time seems to be a better presentation time stamp than pts though
292
293	if (asf_seek_to_msec(asfFile,0) < 0) {
294		printf("Seek to start of stream failed\n");
295	}
296
297	asf_payload_t *payload;
298
299	while (asf_get_packet(asfFile, packet) > 0) {
300		for (int i=0;i<packet->payload_count;i++) {
301			payload = (asf_payload_t *)(&packet->payloads[i]);
302//			printf("Payload %d Stream %d Keyframe %d send time %Ld pts %Ld id %d size %d\n",i+1,payload->stream_number,payload->key_frame, 1000L * bigtime_t(packet->send_time), 1000L * bigtime_t(payload->pts), payload->media_object_number, payload->datalen);
303			if (payload->stream_number < streams.size()) {
304				streams[payload->stream_number].AddPayload(payload->media_object_number, payload->key_frame, 1000L * payload->pts, payload->datalen, false);
305			}
306		}
307	}
308
309	for (uint32 i=0;i<streams.size();i++) {
310		streams[i].AddPayload(0, false, 0, 0, true);
311		streams[i].setDuration(1000L * (packet->send_time + packet->duration));
312	}
313
314	if (asf_seek_to_msec(asfFile,0) < 0) {
315		printf("Seek to start of stream failed\n");
316	}
317}
318
319bool
320ASFFileReader::GetNextChunkInfo(uint32 streamIndex, uint32 pFrameNo,
321	char **buffer, uint32 *size, bool *keyframe, bigtime_t *pts)
322{
323	// Ok, Need to join payloads together that have the same payload->media_object_number
324	asf_payload_t *payload;
325	int64_t seekResult;
326	int packetSize;
327
328	IndexEntry indexEntry = GetIndex(streamIndex, pFrameNo);
329
330	if (indexEntry.noPayloads == 0) {
331		// No index entry
332		printf("No Index entry for frame %ld\n",pFrameNo);
333		return false;
334	}
335
336//	printf("Stream %ld need pts %Ld, packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration));
337
338	if (1000LL * packet->send_time > indexEntry.pts || 1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
339		seekResult = asf_seek_to_msec(asfFile, indexEntry.pts/1000);
340		if (seekResult >= 0) {
341//			printf("Stream %ld seeked to %Ld got %Ld\n",streamIndex,indexEntry.pts, 1000L * seekResult);
342			packetSize = asf_get_packet(asfFile, packet);
343			if (packetSize <= 0) {
344				printf("Failed to Get Packet after seek result (%d)\n",packetSize);
345				return false;
346			}
347		} else if (seekResult == ASF_ERROR_SEEKABLE) {
348			// Stream not seekeable.  Is what we want forward in the stream, if so seek using Get Packet
349			if (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
350				while (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
351					packetSize = asf_get_packet(asfFile, packet);
352					if (packetSize <= 0) {
353						printf("Failed to Seek using Get Packet result (%d)\n",packetSize);
354						return false;
355					}
356//					printf("Stream %ld searching forward for pts %Ld, got packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration));
357				}
358			} else {
359				// seek to 0 and read forward, going to be a killer on performance
360				seekResult = asf_seek_to_msec(asfFile, 0);
361				while (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
362					packetSize = asf_get_packet(asfFile, packet);
363					if (packetSize <= 0) {
364						printf("Failed to Seek using Get Packet result (%d)\n",packetSize);
365						return false;
366					}
367//					printf("Stream %ld searching forward from 0 for pts %Ld, got packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration));
368				}
369			}
370		} else {
371			printf("Seek failed\n");
372			return false;
373		}
374	}
375
376	// fillin some details
377	*size = indexEntry.dataSize;
378	*keyframe = indexEntry.keyFrame;
379	*pts = indexEntry.pts;
380
381	uint32 expectedPayloads = indexEntry.noPayloads;
382	uint32 offset = 0;
383
384	for (int i=0;i<packet->payload_count;i++) {
385		payload = (asf_payload_t *)(&packet->payloads[i]);
386		// find the first payload matching the id we want and then
387		// combine the next x payloads where x is the noPayloads in indexEntry
388		if (payload->media_object_number == indexEntry.id && payload->stream_number == streamIndex) {
389			// copy data to buffer
390			memcpy(*buffer + offset, payload->data, payload->datalen);
391			offset += payload->datalen;
392			expectedPayloads--;
393
394			if (expectedPayloads == 0) {
395				return true;
396			}
397		}
398	}
399
400	// combine packets into a single buffer
401	packetSize = asf_get_packet(asfFile, packet);
402	while ((packetSize > 0) && (expectedPayloads > 0)) {
403		for (int i=0;i<packet->payload_count;i++) {
404			payload = (asf_payload_t *)(&packet->payloads[i]);
405			// find the first payload matching the id we want and then
406			// combine the next x payloads where x is the noPayloads in indexEntry
407			if (payload->media_object_number == indexEntry.id && payload->stream_number == streamIndex) {
408				// copy data to buffer
409				memcpy(*buffer + offset, payload->data, payload->datalen);
410				offset += payload->datalen;
411				expectedPayloads--;
412				if (expectedPayloads == 0) {
413					return true;
414				}
415			}
416		}
417		packetSize = asf_get_packet(asfFile, packet);
418	}
419
420	if (packetSize == ASF_ERROR_EOF) {
421		printf("Unexpected EOF file truncated?\n");
422	} else {
423		printf("EOF? %ld,%d\n",expectedPayloads, packetSize);
424	}
425
426	return false;
427}
428
429
430/* static */
431bool
432ASFFileReader::IsSupported(BPositionIO *source)
433{
434	// Read first 4 bytes and if they match 30 26 b2 75 we have a asf file
435	uint8 header[4];
436	bool supported = false;
437
438	off_t position = source->Position();
439
440	if (source->Read(&header[0],4) == 4) {
441		supported = header[0] == 0x30 &&
442					header[1] == 0x26 &&
443					header[2] == 0xb2 &&
444					header[3] == 0x75;
445	}
446
447	source->Seek(position,SEEK_SET);
448
449	return supported;
450}
451
452/* static */
453int32_t
454ASFFileReader::read(void *opaque, void *buffer, int32_t size)
455{
456	// opaque is the BPositionIO
457	return reinterpret_cast<BPositionIO *>(opaque)->Read(buffer, size);
458}
459
460/* static */
461int32_t
462ASFFileReader::write(void *opaque, void *buffer, int32_t size)
463{
464	return reinterpret_cast<BPositionIO *>(opaque)->Write(buffer, size);
465}
466
467/* static */
468int64_t
469ASFFileReader::seek(void *opaque, int64_t offset)
470{
471	return reinterpret_cast<BPositionIO *>(opaque)->Seek(offset, SEEK_SET);
472}
473