1 /*****************************************************************************
2  *
3  * Copyright 2016 Varol Okan. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  ****************************************************************************/
18 
19 #include <iostream>
20 #include <assert.h>
21 #include <string.h>
22 
23 #include "constants.h"
24 #include "container.h"
25 
26 #include "sa3d.h"
27 
28 
Container(uint32_t iPadding)29 Container::Container ( uint32_t iPadding )
30    : Box ( )
31 {
32   m_iType = constants::Container;
33   m_iPadding = iPadding;
34 }
35 
~Container()36 Container::~Container ( )
37 {
38 }
39 
load(std::fstream & fs,uint32_t iPos,uint32_t iEnd)40 Box *Container::load ( std::fstream &fs, uint32_t iPos, uint32_t iEnd )
41 {
42 //  if ( iPos == 0 )
43 //       iPos = fs.tellg ( );
44 
45   fs.seekg ( iPos );
46   uint32_t iHeaderSize = 8;
47   uint32_t iSize = readUint32 ( fs );
48   char name[4];
49   fs.read ( name, 4 );
50 
51   int32_t iArrSize = (int32_t)(sizeof(constants::CONTAINERS_LIST)/sizeof(constants::CONTAINERS_LIST[0]));
52   bool bIsBox = true;
53   for ( auto t=0; t<iArrSize; t++ )  {
54     if ( memcmp ( name, constants::CONTAINERS_LIST[t], 4 ) == 0 )  {
55       bIsBox = false;
56       break;
57     }
58   }
59 
60   // Handle the mp4a decompressor setting (wave -> mp4a).
61   if ( memcmp ( name, constants::TAG_MP4A, 4 ) == 0 && iSize == 12 )
62     bIsBox = true;
63 
64   if ( bIsBox )  {
65     if ( memcmp ( name, constants::TAG_SA3D, 4 ) == 0 )
66       return SA3DBox::load ( fs, iPos, iEnd );
67     return Box::load ( fs, iPos, iEnd );
68   }
69 
70   if( iSize == 1 )  {
71     iSize = (uint32_t)readUint64 ( fs );
72     iHeaderSize = 16;
73   }
74 
75   if ( iSize < 8 )  {
76     std::cerr << "Error, invalid size " << iSize << " in " << name << " at " << iPos << std::endl;
77     return NULL;
78   }
79 
80   if ( iPos + iSize > iEnd )  {
81     std::cerr << "Error: Container box size exceeds bounds." << std::endl;
82     return NULL;
83   }
84 
85   uint32_t iPadding = 0;
86   if ( memcmp ( name, constants::TAG_STSD, 4 ) == 0 )
87     iPadding = 8;
88 
89   uint32_t iCurrentPos = 0;
90   int16_t  iSampleDescVersion = 0;
91   iArrSize = (int32_t)(sizeof(constants::SOUND_SAMPLE_DESCRIPTIONS)/sizeof(constants::SOUND_SAMPLE_DESCRIPTIONS[0]));
92   for ( auto t=0; t<iArrSize; t++ )  {
93     if ( memcmp ( name, constants::SOUND_SAMPLE_DESCRIPTIONS[t], 4 ) == 0 )  {
94       iCurrentPos = fs.tellg ( );
95       fs.seekg ( iCurrentPos + 8 );
96       fs.read  ( (char *)&iSampleDescVersion, 2 );
97       iSampleDescVersion = be16toh ( iSampleDescVersion );
98       fs.seekg ( iCurrentPos );
99 
100       switch ( iSampleDescVersion )  {
101       case 0:
102         iPadding = 28;
103       break;
104       case 1:
105         iPadding = 28 + 16;
106       break;
107       case 2:
108         iPadding = 64;
109       break;
110       default:
111         std::cerr << "Unsupported sample description version:" << iSampleDescVersion << std::endl;
112       break;
113       }
114     }
115   }
116   Container *pNewBox = new Container ( );
117   memcpy ( pNewBox->m_name, name, 4 );
118   pNewBox->m_iPosition    = iPos;
119   pNewBox->m_iHeaderSize  = iHeaderSize;
120   pNewBox->m_iContentSize = iSize - iHeaderSize;
121   pNewBox->m_iPadding     = iPadding;
122   pNewBox->m_listContents = load_multiple ( fs, iPos + iHeaderSize + iPadding, iPos + iSize );
123 
124   if ( pNewBox->m_listContents.empty ( ) )  {
125     delete pNewBox;
126     return NULL;
127   }
128 
129   return pNewBox;
130 }
131 
load_multiple(std::fstream & fs,uint32_t iPos,uint32_t iEnd)132 std::vector<Box *> Container::load_multiple ( std::fstream &fs, uint32_t iPos, uint32_t iEnd )
133 {
134   std::vector<Box *> list, empty;
135   while ( iPos < iEnd )  {
136     Box *pBox = load ( fs, iPos, iEnd );
137     if ( ! pBox )  {
138       std::cerr << "Error, failed to load box." << std::endl;
139       clear ( list );
140       return empty;
141     }
142     list.push_back ( pBox );
143     iPos = pBox->m_iPosition + pBox->size ( );
144   }
145   return list;
146 }
147 
resize()148 void Container::resize ( )
149 {
150   // Recomputes the box size and recurses on contents."""
151   m_iContentSize = m_iPadding;
152   std::vector<Box *>::iterator it = m_listContents.begin ( );
153   while ( it != m_listContents.end ( ) )  {
154     Box *pBox = *it++;
155     if ( pBox->type ( ) == constants::Container )  {
156       Container *p = (Container *)pBox;
157       p->resize ( );
158     }
159     m_iContentSize += pBox->size ( );
160   }
161 }
162 
print_structure(const char * pIndent)163 void Container::print_structure ( const char *pIndent )
164 {
165   // Prints the box structure and recurses on contents."""
166   uint32_t iSize1 = m_iHeaderSize;
167   uint32_t iSize2 = m_iContentSize;
168   std::cout <<  "{" << pIndent << "} {" << name ( ) << "} [{" << iSize1 << "}, {" << iSize2 << "}]" << std::endl;
169 
170   int32_t iCount = m_listContents.size ( );
171   std::string strIndent = pIndent;
172   std::vector<Box *>::iterator it = m_listContents.begin ( );
173   while ( it != m_listContents.end ( ) )  {
174     Box *pBox = *it++;
175     if ( ! pBox )
176       continue;
177     strIndent.replace ( strIndent.begin ( ), strIndent.end ( ), "├", "│" );
178     strIndent.replace ( strIndent.begin ( ), strIndent.end ( ), "└", " " );
179     strIndent.replace ( strIndent.begin ( ), strIndent.end ( ), "─", " " );
180 
181     if ( --iCount <= 0 )
182       strIndent += " └──";
183     else
184       strIndent += " ├──";
185     pBox->print_structure ( strIndent.c_str ( ) );
186   }
187 }
188 
remove(const char * pName)189 void Container::remove ( const char *pName )
190 {
191   std::vector<Box *> list;
192   m_iContentSize = 0;
193   std::vector<Box *>::iterator it = m_listContents.begin ( );
194   while ( it != m_listContents.end ( ) )  {
195     Box *pBox = *it++;
196     if ( ! pBox )
197       continue;
198     if ( memcmp ( pName, pBox->m_name, 4 ) != 0 )  {
199       list.push_back ( pBox );
200 
201       if ( pBox->type ( ) == constants::Container )  {
202         Container *p = (Container *)pBox;
203         p->remove ( pName );
204       }
205       m_iContentSize += pBox->size ( );
206     }
207     else
208       delete pBox;
209   }
210   m_listContents = list;
211 }
212 
add(Box * pElement)213 bool Container::add ( Box *pElement )
214 {
215   // Adds an element, merging with containers of the same type.
216   std::vector<Box *>::iterator it = m_listContents.begin ( );
217   while ( it != m_listContents.end ( ) )  {
218     Box *pBox = *it++;
219     if ( memcmp ( pElement->m_name, pBox->m_name, 4 ) == 0 )  {
220       if ( pBox->type ( ) == constants::ContainerLeaf )  {
221         Container *p = (Container *)pBox;
222         return p->merge ( pElement );
223       }
224       std::cerr << "Error, cannot merge leafs." << std::endl;
225       return false;
226     }
227   }
228   m_listContents.push_back ( pElement );
229   return true;
230 }
231 
merge(Box * pElem)232 bool Container::merge ( Box *pElem )
233 {
234   assert ( pElem->type ( ) == constants::Container ); // isinstance(element, container_box))
235   Container *pElement = (Container *)pElem;
236   // Merges structure with container.
237   int iRet = memcmp ( m_name, pElement->m_name, 4 );
238   assert ( iRet == 0 );
239   std::vector<Box *>::iterator it = pElement->m_listContents.begin ( );
240   while ( it != pElement->m_listContents.end ( ) )  {
241     Box *pSubElement = *it++;
242     if ( ! add ( pSubElement ) )
243       return false;
244   }
245   return true;
246 }
247 
save(std::fstream & fsIn,std::fstream & fsOut,int32_t iDelta)248 void Container::save ( std::fstream &fsIn, std::fstream &fsOut, int32_t iDelta )
249 {
250   // Saves box to out_fh reading uncached content from in_fh.
251   // iDelta : file change size for updating stco and co64 files.
252   if ( m_iHeaderSize == 16 )  {
253     writeUint32 ( fsOut, 1 );
254     fsOut.write ( m_name, 4 );
255     writeUint64 ( fsOut, size ( ) );
256   }
257   else if ( m_iHeaderSize == 8 )  {
258     writeUint32 ( fsOut, size ( ) );
259     fsOut.write ( m_name, 4 );
260   }
261   if ( m_iPadding > 0 )  {
262     fsIn.seekg ( content_start ( ) );
263     Box::tag_copy ( fsIn, fsOut, m_iPadding );
264   }
265 
266   std::vector<Box *>::iterator it = m_listContents.begin ( );
267   while ( it != m_listContents.end ( ) )  {
268     Box *pElement = *it++;
269     if ( ! pElement )
270       continue;
271     pElement->save ( fsIn, fsOut, iDelta );
272   }
273 }
274 
275