1/****************************************************************************
2** libmatroska : parse Matroska files, see http://www.matroska.org/
3**
4** <file/class description>
5**
6** Copyright (C) 2002-2003 Steve Lhomme.  All rights reserved.
7**
8** This file is part of libmatroska.
9**
10** This library is free software; you can redistribute it and/or
11** modify it under the terms of the GNU Lesser General Public
12** License as published by the Free Software Foundation; either
13** version 2.1 of the License, or (at your option) any later version.
14**
15** This library is distributed in the hope that it will be useful,
16** but WITHOUT ANY WARRANTY; without even the implied warranty of
17** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18** Lesser General Public License for more details.
19**
20** You should have received a copy of the GNU Lesser General Public
21** License along with this library; if not, write to the Free Software
22** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23**
24** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.**
25** Contact license@matroska.org if any conditions of this licensing are
26** not clear to you.
27**
28**********************************************************************/
29
30/*!
31    \file
32    \version \$Id: FileKax.cpp 640 2004-07-09 21:05:36Z mosu $
33    \author Steve Lhomme     <robux4 @ users.sf.net>
34*/
35//#include "StdInclude.h"
36#include "matroska/FileKax.h"
37//#include "Cluster.h"
38//#include "Track.h"
39//#include "Block.h"
40//#include "Frame.h"
41//#include "Version.h"
42
43START_LIBMATROSKA_NAMESPACE
44
45//typedef Track *TrackID;
46
47FileMatroska::FileMatroska(IOCallback & output)
48 :myFile(output)
49#ifdef OLD
50 ,myCurrReadBlock(NULL)
51 ,myMinClusterSize(5*1024)      // 5KB is the min size of a cluster
52 ,myMaxClusterSize(2*1024*1024) // 2MB is the max size of a cluster
53 ,myCurrReadBlockTrack(0)
54 ,myCurrWriteCluster(2*1024*1024) // myMaxClusterSize
55 ,myCurrReadCluster(NULL)
56 ,myReadBlockNumber(0)
57#endif // OLD
58{
59#ifdef OLD
60    myStreamInfo.MainHeaderSize = TypeHeader::default_size() +
61		ActualHeader::default_size() +
62		ExtendedInfo::default_size() +
63		ContentInfo::default_size();
64    myStreamInfo.TrackEntrySize = Track::default_size();
65    myStreamInfo.BlockHeadSize = BLOCK_HEADER_SIZE;
66    myStreamInfo.ClusterHeadSize = CLUSTER_HEADER_SIZE;
67    myStreamInfo.ClusterFootSize = CLUSTER_TRAILER_SIZE;
68#endif // OLD
69}
70
71FileMatroska::~FileMatroska()
72{
73//    if (myCurrCluster != NULL)
74//	throw 0; // there are some data left to write
75//    if (myCurrReadCluster != NULL || myCurrReadBlock != NULL)
76//	throw 0; // there are some data left to write
77}
78
79#ifdef OLD
80void FileMatroska::SetMaxClusterSize(const uint32 value)
81{
82    myMaxClusterSize = value;
83    myCurrWriteCluster.setMaxSize(value);
84}
85
86void FileMatroska::Close(const uint32 aTimeLength)
87{
88    Flush();
89
90    // get the file size
91    myFile.setFilePointer(0,seek_end);
92    myMainHeader.type_SetSize(myFile.getFilePointer());
93
94    // rewrite the header at the beginning
95    myFile.setFilePointer(0,seek_beginning);
96
97    // get the Track-entry size
98    uint32 track_entries_size = 0;
99    for (size_t i=0; i<myTracks.size(); i++)
100    {
101	track_entries_size += myTracks[i]->default_size();
102    }
103
104    myStreamInfo.TrackEntriesSize = track_entries_size;
105    myStreamInfo.TimeLength = aTimeLength;
106    myMainHeader.Render(myFile, myStreamInfo);
107
108    for (i=0; i<myTracks.size(); i++)
109    {
110	delete myTracks[i];
111    }
112}
113
114/*!
115    \warning after rendering the head, some parameters are locked
116*/
117uint32 FileMatroska::RenderHead(const std::string & aEncoderApp)
118{
119    try {
120	uint32 track_entries_size = 0;
121	for (size_t i=0; i<myTracks.size(); i++)
122	{
123	    track_entries_size += myTracks[i]->default_size();
124	}
125
126	std::string aStr = LIB_NAME;
127	aStr += " ";
128	aStr += VERSION;
129	myStreamInfo.EncoderLib = aStr;
130
131	myStreamInfo.EncoderApp = aEncoderApp;
132
133	myStreamInfo.TrackEntryPosition = 0 + myStreamInfo.MainHeaderSize;
134	myStreamInfo.TrackEntriesSize = myTracks.size() * myStreamInfo.TrackEntrySize;
135
136	myStreamInfo.CodecEntryPosition = myStreamInfo.MainHeaderSize + myStreamInfo.TrackEntriesSize;
137	myStreamInfo.CodecEntrySize = 4;
138	for (i=0; i<myTracks.size(); i++)
139	{
140	    myStreamInfo.CodecEntrySize += myTracks[i]->CodecSize();
141	}
142
143	// Main Header
144	uint32 result = myMainHeader.Render(myFile, myStreamInfo);
145
146	// Track Entries
147	for (i=0; i<myTracks.size(); i++)
148	{
149	    myTracks[i]->RenderEntry(myFile, i+1);
150	}
151	myStreamInfo.ClusterPosition = myStreamInfo.CodecEntryPosition + myStreamInfo.CodecEntrySize;
152
153	// Codec Header
154	result = CodecHeader::Render(myFile, myTracks);
155
156	return result;
157    }
158    catch (exception & Ex)
159    {
160	throw Ex;
161    }
162}
163
164/*!
165    \return 0 if the track was not created, or a valid track number
166*/
167Track * FileMatroska::CreateTrack(const track_type aType)
168{
169    myTracks.push_back(new Track(aType));
170    return myTracks.back();
171}
172
173/*Track *FileMatroska::findTrack(Track * aTrack) const
174{
175    for (size_t i=0; i<myTracks.size(); i++)
176    {
177	if (myTracks[i] == aTrack)
178	    return myTracks[i];
179    }
180
181    return NULL;
182}*/
183
184void FileMatroska::track_SetName(Track * aTrack, const std::string & aName)
185{
186    if (IsMyTrack(aTrack))
187    {
188        aTrack->SetName(aName);
189    }
190}
191
192void FileMatroska::track_SetLaced(Track * aTrack, const bool bLaced)
193{
194    if (IsMyTrack(aTrack))
195    {
196        aTrack->SetLaced(bLaced);
197    }
198}
199
200bool FileMatroska::AddFrame(Track * aTrack, const uint32 aTimecode, const binary *aFrame, const uint32 aFrameSize,
201					   const bool aKeyFrame, const bool aBFrame)
202{
203    try {
204	// make sure we know that track
205	if (IsMyTrack(aTrack))
206	{
207	    // pass the cluster to the track
208	    // handle the creation of a new cluster if needed
209	    if (aTrack->AddFrame(aTimecode, aFrame, aFrameSize, aKeyFrame, aBFrame))
210	    {
211		while (!aTrack->SerialiseBlock(myCurrWriteCluster))
212		{
213		    /// \todo handle errors
214		    uint32 aNbBlock;
215		    myStreamInfo.ClusterSize += myCurrWriteCluster.Render(myFile, aNbBlock);
216		    myStreamInfo.NumberBlock += aNbBlock;
217		    myCurrWriteCluster.Flush();
218		}
219	    }
220		return true;
221	}
222	return false;
223    }
224    catch (exception & Ex)
225    {
226	throw Ex;
227    }
228}
229
230void FileMatroska::Flush()
231{
232    uint32 aNbBlock;
233    myStreamInfo.ClusterSize += myCurrWriteCluster.Render(myFile,aNbBlock);
234    myStreamInfo.NumberBlock += aNbBlock;
235}
236
237uint32 FileMatroska::ReadHead()
238{
239    try {
240	uint32 result = myMainHeader.Read(myFile, myStreamInfo);
241
242	return result;
243    }
244    catch (exception & Ex)
245    {
246	throw Ex;
247    }
248}
249
250uint32 FileMatroska::ReadTracks()
251{
252    try {
253	uint32 result = 0;
254
255	// seek to the start of the Track Entries
256	myFile.setFilePointer(myStreamInfo.TrackEntryPosition);
257	// get the number of Track Entries
258	uint8 TrackNumber = myStreamInfo.TrackEntriesSize / myStreamInfo.TrackEntrySize;
259	// read all the Track Entries
260	myTracks.clear();
261	for (uint8 TrackIdx = 0; TrackIdx<TrackNumber; TrackIdx ++) {
262	    Track * tmpTrack = Track::ReadEntry(myFile, TrackIdx+1, myStreamInfo);
263	    if (tmpTrack == NULL)
264		throw 0;
265
266	    myTracks.push_back(tmpTrack);
267	}
268
269	return result;
270    }
271    catch (exception & Ex)
272    {
273	throw Ex;
274    }
275}
276
277uint32 FileMatroska::ReadCodec()
278{
279    try {
280	// seek to the start of the Track Entries
281	myFile.setFilePointer(myStreamInfo.CodecEntryPosition);
282
283	uint32 result = CodecHeader::Read(myFile, myTracks);
284
285	return result;
286    }
287    catch (exception & Ex)
288    {
289	throw Ex;
290    }
291}
292
293inline bool FileMatroska::IsMyTrack(const Track * aTrack) const
294{
295    if (aTrack == 0)
296	throw 0;
297
298    for (std::vector<Track*>::const_iterator i = myTracks.begin(); i != myTracks.end(); i ++)
299    {
300	if (*i == aTrack)
301	    break;
302    }
303
304    if (i != myTracks.end())
305	return true;
306    else
307	return false;
308}
309
310void FileMatroska::SelectReadingTrack(Track * aTrack, bool select)
311{
312    if (IsMyTrack(aTrack))
313    {
314	// here we have the right track
315	// check if it's not already selected
316	for (std::vector<uint8>::iterator j = mySelectedTracks.begin();
317	    j != mySelectedTracks.end(); j ++)
318	{
319	    if (*j == aTrack->TrackNumber())
320		break;
321	}
322
323	if (select && j == mySelectedTracks.end())
324	    mySelectedTracks.push_back(aTrack->TrackNumber());
325	else if (!select && j != mySelectedTracks.end())
326	    mySelectedTracks.erase(j);
327
328	std::sort(mySelectedTracks.begin(), mySelectedTracks.end());
329    }
330}
331
332inline bool FileMatroska::IsReadingTrack(const uint8 aTrackNumber) const
333{
334    for (std::vector<uint8>::const_iterator trackIdx = mySelectedTracks.begin();
335         trackIdx != mySelectedTracks.end() && *trackIdx < aTrackNumber;
336	 trackIdx++)
337    {}
338
339    if (trackIdx == mySelectedTracks.end())
340	return false;
341    else
342	return true;
343}
344
345//
346
347void FileMatroska::Track_GetInfo(const Track * aTrack, TrackInfo & aTrackInfo) const
348{
349    if (IsMyTrack(aTrack))
350    {
351	aTrack->GetInfo(aTrackInfo);
352    }
353}
354
355// Audio related getters/setters
356
357void FileMatroska::Track_GetInfo_Audio(const Track * aTrack, TrackInfoAudio & aTrackInfo) const
358{
359    if (IsMyTrack(aTrack))
360    {
361	aTrack->GetInfoAudio(aTrackInfo);
362    }
363}
364
365void FileMatroska::Track_SetInfo_Audio(Track * aTrack, const TrackInfoAudio & aTrackInfo)
366{
367    if (IsMyTrack(aTrack))
368    {
369	aTrack->SetInfoAudio(aTrackInfo);
370    }
371}
372
373// Video related getters/setters
374
375void FileMatroska::Track_GetInfo_Video(const Track * aTrack, TrackInfoVideo & aTrackInfo) const
376{
377    if (IsMyTrack(aTrack))
378    {
379	aTrack->GetInfoVideo(aTrackInfo);
380    }
381}
382
383void FileMatroska::Track_SetInfo_Video(Track * aTrack, const TrackInfoVideo & aTrackInfo)
384{
385    if (IsMyTrack(aTrack))
386    {
387	aTrack->SetInfoVideo(aTrackInfo);
388    }
389}
390
391/*!
392    \todo exit when there is no Block left
393*/
394bool FileMatroska::ReadFrame(Track * & aTrack, uint32 & aTimecode, const binary * & aFrame, uint32 & aFrameSize,
395						bool & aKeyFrame, bool & aBFrame)
396{
397    if (myCurrReadBlockTrack == 0)
398    {
399	do {
400	    if (myReadBlockNumber >= myStreamInfo.NumberBlock)
401	    {
402//		myReadBlockNumber = myStreamInfo.NumberBlock;
403		return false;
404	    }
405
406	    // get the next frame in the file
407	    if (!myCurrReadCluster.BlockLeft())
408	    {
409		myCurrReadCluster.Flush();
410		try {
411		    myCurrReadCluster.FindHead(myFile);
412		}
413		catch (exception & Ex)
414		{
415		    return false;
416		}
417	    }
418
419	    myCurrReadCluster.GetBlock( myCurrReadBlock, myCurrReadBlockSize, myCurrReadBlockTrack );
420	    myReadBlockNumber++;
421	} while (!IsReadingTrack(myCurrReadBlockTrack));
422
423	// get the track associated (normally from myTracks)
424	aTrack = myTracks[myCurrReadBlockTrack-1];
425	// get the next frame from the current block
426	aTrack->HandleBlock(myCurrReadBlock, myCurrReadBlockSize);
427    }
428    else
429    {
430	// get the track associated (normally from myTracks)
431	aTrack = myTracks[myCurrReadBlockTrack-1];
432    }
433
434    Frame *  myReadFrame;
435    aTrack->GetNextFrame(aTimecode, myReadFrame, aKeyFrame, aBFrame);
436    aFrame = myReadFrame->buf();
437    aFrameSize = myReadFrame->length();
438
439    if (aTrack->NoFrameLeft())
440    {
441	aTrack->FlushBlock();
442	myCurrReadBlockTrack = 0;
443    }
444
445    return true;
446}
447#endif // OLD
448
449END_LIBMATROSKA_NAMESPACE
450