AviFile.cpp

00001 #include "StdAfx.h"
00002 #include "AviFile.h"
00003 
00004 //
00005 // CAviFile
00006 //
00007 
00008 CAviFile::CAviFile(IAsyncReader* pAsyncReader, HRESULT& hr)
00009         : CBaseSplitterFile(pAsyncReader, hr)
00010 {
00011         if(FAILED(hr)) return;
00012         hr = Init();
00013 }
00014 
00015 template<typename T> 
00016 HRESULT CAviFile::Read(T& var, int offset)
00017 {
00018         memset(&var, 0, sizeof(var));
00019         HRESULT hr = Read((BYTE*)&var + offset, sizeof(var) - offset);
00020         return hr;
00021 }
00022 
00023 HRESULT CAviFile::Init()
00024 {
00025         Seek(0);
00026         DWORD dw[3];
00027         if(S_OK != Read(dw) || dw[0] != FCC('RIFF') || (dw[2] != FCC('AVI ') && dw[2] != FCC('AVIX')))
00028                 return E_FAIL;
00029 
00030         Seek(0);
00031         HRESULT hr = Parse(0, GetLength());
00032         if(m_movis.GetCount() == 0) // FAILED(hr) is allowed as long as there was a movi chunk found
00033                 return E_FAIL;
00034 
00035         if(m_avih.dwStreams == 0 && m_strms.GetCount() > 0)
00036                 m_avih.dwStreams = m_strms.GetCount();
00037 
00038         if(m_avih.dwStreams != m_strms.GetCount())
00039                 return E_FAIL;
00040 
00041         for(int i = 0; i < (int)m_avih.dwStreams; i++)
00042         {
00043                 strm_t* s = m_strms[i];
00044                 if(s->strh.fccType != FCC('auds')) continue;
00045                 WAVEFORMATEX* wfe = (WAVEFORMATEX*)s->strf.GetData();
00046                 if(wfe->wFormatTag == 0x55 && wfe->nBlockAlign == 1152 
00047                 && s->strh.dwScale == 1 && s->strh.dwRate != wfe->nSamplesPerSec)
00048                 {
00049                         // correcting encoder bugs...
00050                         s->strh.dwScale = 1152;
00051                         s->strh.dwRate = wfe->nSamplesPerSec;
00052                 }
00053         }
00054 
00055         if(FAILED(BuildIndex()))
00056                 EmptyIndex();
00057 
00058         return S_OK;
00059 }
00060 
00061 HRESULT CAviFile::Parse(DWORD parentid, __int64 end)
00062 {
00063         HRESULT hr = S_OK;
00064 
00065         CAutoPtr<strm_t> strm;
00066 
00067         while(S_OK == hr && GetPos() < end)
00068         {
00069                 UINT64 pos = GetPos();
00070 
00071                 DWORD id = 0, size;
00072                 if(S_OK != Read(id) || id == 0)
00073                         return E_FAIL;
00074 
00075                 if(id == FCC('RIFF') || id == FCC('LIST'))
00076                 {
00077                         if(S_OK != Read(size) || S_OK != Read(id))
00078                                 return E_FAIL;
00079 
00080                         size += (size&1) + 8;
00081 
00082                         TRACE(_T("CAviFile::Parse(..): LIST '%c%c%c%c'\n"), 
00083                                 TCHAR((id>>0)&0xff), 
00084                                 TCHAR((id>>8)&0xff), 
00085                                 TCHAR((id>>16)&0xff),
00086                                 TCHAR((id>>24)&0xff));
00087 
00088                         if(id == FCC('movi'))
00089                         {
00090                                 m_movis.AddTail(pos);
00091                         }
00092                         else
00093                         {
00094                                 hr = Parse(id, pos + size);
00095                         }
00096                 }
00097                 else
00098                 {
00099                         if(S_OK != Read(size))
00100                                 return E_FAIL;
00101 
00102                         TRACE(_T("CAviFile::Parse(..): '%c%c%c%c'\n"), 
00103                                 TCHAR((id>>0)&0xff), 
00104                                 TCHAR((id>>8)&0xff), 
00105                                 TCHAR((id>>16)&0xff),
00106                                 TCHAR((id>>24)&0xff));
00107 
00108                         if(parentid == FCC('INFO') && size > 0)
00109                         {
00110                                 switch(id)
00111                                 {
00112                                 case FCC('IARL'): // Archival Location. Indicates where the subject of the file is archived.
00113                                 case FCC('IART'): // Artist. Lists the artist of the original subject of the file; for example, “Michaelangelo.”
00114                                 case FCC('ICMS'): // Commissioned. Lists the name of the person or organization that commissioned the subject of the file; for example, “Pope Julian II.”
00115                                 case FCC('ICMT'): // Comments. Provides general comments about the file or the subject of the file. If the comment is several sentences long, end each sentence with a period. Do not include new-line characters.
00116                                 case FCC('ICOP'): // Copyright. Records the copyright information for the file; for example, “Copyright Encyclopedia International 1991.” If there are multiple copyrights, separate them by a semicolon followed by a space.
00117                                 case FCC('ICRD'): // Creation date. Specifies the date the subject of the file was created. List dates in year-month-day format, padding one-digit months and days with a zero on the left; for example, “1553-05-03” for May 3, 1553.
00118                                 case FCC('ICRP'): // Cropped. Describes whether an image has been cropped and, if so, how it was cropped; for example, “lower-right corner.”
00119                                 case FCC('IDIM'): // Dimensions. Specifies the size of the original subject of the file; for example, “8.5 in h, 11 in w.”
00120                                 case FCC('IDPI'): // Dots Per Inch. Stores dots per inch setting of the digitizer used to produce the file, such as “300.”
00121                                 case FCC('IENG'): // Engineer. Stores the name of the engineer who worked on the file. If there are multiple engineers, separate the names by a semicolon and a blank; for example, “Smith, John; Adams, Joe.”
00122                                 case FCC('IGNR'): // Genre. Describes the original work, such as “landscape,” “portrait,” “still life,” etc.
00123                                 case FCC('IKEY'): // Keywords. Provides a list of keywords that refer to the file or subject of the file. Separate multiple keywords with a semicolon and a blank; for example, “Seattle; aerial view; scenery.”
00124                                 case FCC('ILGT'): // Lightness. Describes the changes in lightness settings on the digitizer required to produce the file. Note that the format of this information depends on hardware used.
00125                                 case FCC('IMED'): // Medium. Describes the original subject of the file, such as “computer image,” “drawing,” “lithograph,” and so on.
00126                                 case FCC('INAM'): // Name. Stores the title of the subject of the file, such as “Seattle From Above.”
00127                                 case FCC('IPLT'): // Palette Setting. Specifies the number of colors requested when digitizing an image, such as “256.”
00128                                 case FCC('IPRD'): // Product. Specifies the name of the title the file was originally intended for, such as “Encyclopedia of Pacific Northwest Geography.”
00129                                 case FCC('ISBJ'): // Subject. Describes the contents of the file, such as “Aerial view of Seattle.”
00130                                 case FCC('ISFT'): // Software. Identifies the name of the software package used to create the file, such as “Microsoft WaveEdit.”
00131                                 case FCC('ISHP'): // Sharpness. Identifies the changes in sharpness for the digitizer required to produce the file (the format depends on the hardware used).
00132                                 case FCC('ISRC'): // Source. Identifies the name of the person or organization who supplied the original subject of the file; for example, “Trey Research.”
00133                                 case FCC('ISRF'): // Source Form. Identifies the original form of the material that was digitized, such as “slide,” “paper,” “map,” and so on. This is not necessarily the same as IMED.
00134                                 case FCC('ITCH'): // Technician. Identifies the technician who digitized the subject file; for example, “Smith, John.”
00135                                         {
00136                                                 CStringA str;
00137                                                 if(S_OK != Read((BYTE*)str.GetBufferSetLength(size), size)) return E_FAIL;
00138                                                 m_info[id] = str;
00139                                                 break;
00140                                         }
00141                                 }
00142                         }
00143 
00144                         switch(id)
00145                         {
00146                         case FCC('avih'):
00147                                 m_avih.fcc = FCC('avih');
00148                                 m_avih.cb = size;
00149                                 if(S_OK != Read(m_avih, 8)) return E_FAIL;
00150                                 break;
00151                         case FCC('strh'):
00152                                 if(!strm) strm.Attach(new strm_t());
00153                                 strm->strh.fcc = FCC('strh');
00154                                 strm->strh.cb = size;
00155                                 if(S_OK != Read(strm->strh, 8)) return E_FAIL;
00156                                 break;
00157                         case FCC('strn'):
00158                                 if(S_OK != Read((BYTE*)strm->strn.GetBufferSetLength(size), size)) return E_FAIL;
00159                                 break;
00160                         case FCC('strf'):
00161                                 if(!strm) strm.Attach(new strm_t());
00162                                 strm->strf.SetSize(size);
00163                                 if(S_OK != Read(strm->strf.GetData(), size)) return E_FAIL;
00164                                 break;
00165                         case FCC('indx'):
00166                                 if(!strm) strm.Attach(new strm_t());
00167                                 ASSERT(strm->indx == NULL);
00168                                 strm->indx.Attach((AVISUPERINDEX*)new BYTE[size + 8]);
00169                                 strm->indx->fcc = FCC('indx');
00170                                 strm->indx->cb = size;
00171                                 if(S_OK != Read((BYTE*)(AVISUPERINDEX*)strm->indx + 8, size)) return E_FAIL;
00172                                 ASSERT(strm->indx->wLongsPerEntry == 4 && strm->indx->bIndexType == AVI_INDEX_OF_INDEXES);
00173                                 break;
00174                         case FCC('dmlh'):
00175                                 if(S_OK != Read(m_dmlh)) return E_FAIL;
00176                                 break;
00177                         case FCC('vprp'):
00178 //                              if(S_OK != Read(m_vprp)) return E_FAIL;
00179                                 break;
00180                         case FCC('idx1'):
00181                                 ASSERT(m_idx1 == NULL);
00182                                 m_idx1.Attach((AVIOLDINDEX*)new BYTE[size + 8]);
00183                                 m_idx1->fcc = FCC('idx1');
00184                                 m_idx1->cb = size;
00185                                 if(S_OK != Read((BYTE*)(AVIOLDINDEX*)m_idx1 + 8, size)) return E_FAIL;
00186                                 break;
00187                         }
00188 
00189                         size += (size&1) + 8;
00190                 }
00191 
00192                 Seek(pos + size);
00193         }
00194 
00195         if(strm) m_strms.Add(strm);
00196 
00197         return hr;
00198 }
00199 
00200 REFERENCE_TIME CAviFile::GetTotalTime()
00201 {
00202         REFERENCE_TIME t = 0/*10i64*m_avih.dwMicroSecPerFrame*m_avih.dwTotalFrames*/;
00203 
00204         for(int i = 0; i < (int)m_avih.dwStreams; i++)
00205         {
00206                 strm_t* s = m_strms[i];
00207                 REFERENCE_TIME t2 = s->GetRefTime(s->cs.GetCount(), s->totalsize);
00208                 t = max(t, t2);
00209         }
00210 
00211         if(t == 0) t = 10i64*m_avih.dwMicroSecPerFrame*m_avih.dwTotalFrames;
00212 
00213         return(t);
00214 }
00215 
00216 HRESULT CAviFile::BuildIndex()
00217 {
00218         EmptyIndex();
00219 
00220         int nSuperIndexes = 0;
00221 
00222         for(int i = 0; i < (int)m_avih.dwStreams; i++)
00223         {
00224                 strm_t* s = m_strms[i];
00225                 if(s->indx && s->indx->nEntriesInUse > 0) nSuperIndexes++;
00226         }
00227 
00228         if(nSuperIndexes == m_avih.dwStreams)
00229         {
00230                 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00231                 {
00232                         strm_t* s = m_strms[i];
00233 
00234                         AVISUPERINDEX* idx = (AVISUPERINDEX*)s->indx;
00235 
00236                         DWORD nEntriesInUse = 0;
00237 
00238                         for(int j = 0; j < (int)idx->nEntriesInUse; j++)
00239                         {
00240                                 Seek(idx->aIndex[j].qwOffset);
00241 
00242                                 AVISTDINDEX stdidx;
00243                                 if(S_OK != Read((BYTE*)&stdidx, FIELD_OFFSET(AVISTDINDEX, aIndex)))
00244                                 {
00245                                         EmptyIndex();
00246                                         return E_FAIL;
00247                                 }
00248 
00249                                 nEntriesInUse += stdidx.nEntriesInUse;
00250                         } 
00251 
00252                         s->cs.SetSize(nEntriesInUse);
00253 
00254                         DWORD frame = 0;
00255                         UINT64 size = 0;
00256 
00257                         for(int j = 0; j < (int)idx->nEntriesInUse; j++)
00258                         {
00259                                 Seek(idx->aIndex[j].qwOffset);
00260 
00261                                 CAutoPtr<AVISTDINDEX> p((AVISTDINDEX*)new BYTE[idx->aIndex[j].dwSize]);
00262                                 if(!p || S_OK != Read((BYTE*)(AVISTDINDEX*)p, idx->aIndex[j].dwSize)) 
00263                                 {
00264                                         EmptyIndex();
00265                                         return E_FAIL;
00266                                 }
00267 
00268                                 for(int k = 0, l = 0; k < (int)p->nEntriesInUse; k++)
00269                                 {
00270                                         s->cs[frame].size = size;
00271                                         s->cs[frame].filepos = p->qwBaseOffset + p->aIndex[k].dwOffset;
00272                                         s->cs[frame].fKeyFrame = !(p->aIndex[k].dwSize&AVISTDINDEX_DELTAFRAME) 
00273                                                 || s->strh.fccType == FCC('auds');
00274                                         s->cs[frame].fChunkHdr = false;
00275                                         s->cs[frame].orgsize = p->aIndex[k].dwSize&AVISTDINDEX_SIZEMASK;
00276 
00277                                         if(m_idx1)
00278                                         {
00279                                                 s->cs[frame].filepos -= 8;
00280                                                 s->cs[frame].fChunkHdr = true;
00281                                         }
00282 
00283                                         frame++;
00284                                         size += s->GetChunkSize(p->aIndex[k].dwSize&AVISTDINDEX_SIZEMASK);
00285                                 }
00286                         }
00287 
00288                         s->totalsize = size;
00289                 }
00290         }
00291         else if(AVIOLDINDEX* idx = m_idx1)
00292         {
00293                 int len = idx->cb/sizeof(idx->aIndex[0]);
00294 
00295                 UINT64 offset = m_movis.GetHead() + 8;
00296 
00297                 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00298                 {
00299                         strm_t* s = m_strms[i];
00300 
00301                         int nFrames = 0;
00302 
00303                         for(int j = 0; j < len; j++)
00304                         {
00305                                 if(TRACKNUM(idx->aIndex[j].dwChunkId) == i)
00306                                         nFrames++;
00307                         }
00308 
00309                         s->cs.SetSize(nFrames);
00310 
00311                         DWORD frame = 0;
00312                         UINT64 size = 0;
00313 
00314                         for(int j = 0, k = 0; j < len; j++)
00315                         {
00316                                 DWORD TrackNumber = TRACKNUM(idx->aIndex[j].dwChunkId);
00317 
00318                                 if(TrackNumber == i)
00319                                 {
00320                                         if(j == 0 && idx->aIndex[j].dwOffset > offset)
00321                                         {
00322                                                 DWORD id;
00323                                                 Seek(offset + idx->aIndex[j].dwOffset);
00324                                                 Read(id);
00325                                                 if(id != idx->aIndex[j].dwChunkId)
00326                                                 {
00327                                                         TRACE(_T("WARNING: CAviFile::Init() detected absolute chunk addressing in \'idx1\'"));
00328                                                         offset = 0;
00329                                                 }
00330                                         }
00331 
00332                                         s->cs[frame].size = size;
00333                                         s->cs[frame].filepos = offset + idx->aIndex[j].dwOffset;
00334                                         s->cs[frame].fKeyFrame = !!(idx->aIndex[j].dwFlags&AVIIF_KEYFRAME) 
00335                                                 || s->strh.fccType == FCC('auds') // FIXME: some audio index is without any kf flag
00336                                                 || frame == 0; // grrr
00337                                         s->cs[frame].fChunkHdr = j == len-1 || idx->aIndex[j].dwOffset != idx->aIndex[j+1].dwOffset;
00338                                         s->cs[frame].orgsize = idx->aIndex[j].dwSize;
00339 
00340                                         frame++;
00341                                         size += s->GetChunkSize(idx->aIndex[j].dwSize);
00342                                 }
00343                         }
00344 
00345                         s->totalsize = size;
00346                 }
00347         }
00348 
00349         m_idx1.Free();
00350         for(int i = 0; i < (int)m_avih.dwStreams; i++)
00351                 m_strms[i]->indx.Free();
00352 
00353         return S_OK;
00354 }
00355 
00356 void CAviFile::EmptyIndex()
00357 {
00358         for(int i = 0; i < (int)m_avih.dwStreams; i++)
00359         {
00360                 strm_t* s = m_strms[i];
00361                 s->cs.RemoveAll();
00362                 s->totalsize = 0;
00363         }
00364 }
00365 
00366 bool CAviFile::IsInterleaved(bool fKeepInfo)
00367 {
00368         if(m_strms.GetCount() < 2)
00369                 return(true);
00370 /*
00371         if(m_avih.dwFlags&AVIF_ISINTERLEAVED) // not reliable, nandub can write f*cked up files and still sets it
00372                 return(true);
00373 */
00374         for(int i = 0; i < (int)m_avih.dwStreams; i++)
00375                 m_strms[i]->cs2.SetSize(m_strms[i]->cs.GetSize());
00376 
00377         DWORD* curchunks = new DWORD[m_avih.dwStreams];
00378         UINT64* cursizes = new UINT64[m_avih.dwStreams];
00379 
00380         memset(curchunks, 0, sizeof(DWORD)*m_avih.dwStreams);
00381         memset(cursizes, 0, sizeof(UINT64)*m_avih.dwStreams);
00382 
00383         int end = 0;
00384 
00385         while(1)
00386         {
00387                 UINT64 fpmin = _I64_MAX;
00388 
00389                 DWORD n = -1;
00390                 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00391                 {
00392                         int curchunk = curchunks[i];
00393                         CArray<strm_t::chunk>& cs = m_strms[i]->cs;
00394                         if(curchunk >= cs.GetSize()) continue;
00395             UINT64 fp = cs[curchunk].filepos;
00396                         if(fp < fpmin) {fpmin = fp; n = i;}
00397                 }
00398                 if(n == -1) break;
00399 
00400                 strm_t* s = m_strms[n];
00401                 DWORD& curchunk = curchunks[n];
00402                 UINT64& cursize = cursizes[n];
00403 
00404                 if(!s->IsRawSubtitleStream())
00405                 {
00406                         strm_t::chunk2& cs2 = s->cs2[curchunk];
00407                         cs2.t = (DWORD)(s->GetRefTime(curchunk, cursize)>>13); // for comparing later it is just as good as /10000 to get a near [ms] accuracy
00408 //                      cs2.t = (DWORD)(s->GetRefTime(curchunk, cursize)/10000);
00409                         cs2.n = end++;
00410                 }
00411 
00412                 cursize = s->cs[curchunk].size;
00413                 curchunk++;
00414         }
00415 
00416         memset(curchunks, 0, sizeof(DWORD)*m_avih.dwStreams);
00417 
00418         strm_t::chunk2 cs2last = {-1, 0};
00419 
00420         bool fInterleaved = true;
00421 
00422         while(fInterleaved)
00423         {
00424                 strm_t::chunk2 cs2min = {LONG_MAX, LONG_MAX};
00425 
00426                 int n = -1;
00427                 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00428                 {
00429                         int curchunk = curchunks[i];
00430                         if(curchunk >= m_strms[i]->cs2.GetSize()) continue;
00431                         strm_t::chunk2& cs2 = m_strms[i]->cs2[curchunk];
00432                         if(cs2.t < cs2min.t) {cs2min = cs2; n = i;}
00433                 }
00434                 if(n == -1) break;
00435 
00436                 curchunks[n]++;
00437 
00438                 if(cs2last.t >= 0 && abs(cs2min.n - cs2last.n) >= 1000)
00439                         fInterleaved = false;
00440 
00441                 cs2last = cs2min;
00442         }
00443 
00444         delete [] curchunks;
00445         delete [] cursizes;
00446 
00447         if(fInterleaved && !fKeepInfo)
00448         {
00449                 // this is not needed anymore, let's save a little memory then
00450                 for(int i = 0; i < (int)m_avih.dwStreams; i++)
00451                         m_strms[i]->cs2.SetSize(0);
00452         }
00453 
00454         return(fInterleaved);
00455 }
00456 
00457 REFERENCE_TIME CAviFile::strm_t::GetRefTime(DWORD frame, UINT64 size)
00458 {
00459         REFERENCE_TIME rt = -1;
00460 
00461         if(strh.fccType == FCC('auds'))
00462         {
00463                 WAVEFORMATEX* wfe = (WAVEFORMATEX*)strf.GetData();
00464 
00465                 rt = wfe->nBlockAlign && strh.dwRate
00466                         ? ((10000000i64 * size + (wfe->nBlockAlign>>1)) / wfe->nBlockAlign * strh.dwScale + (strh.dwRate>>1)) / strh.dwRate 
00467                         : 0;
00468         }
00469         else
00470         {
00471                 rt = strh.dwRate
00472                         ? 10000i64 * ((1000i64 * frame * strh.dwScale + (strh.dwRate>>1)) / strh.dwRate) // 10000 * (1000 * ... ) because it is less likely to overflow this way
00473                         : 0;
00474         }
00475 
00476         return(rt);
00477 }
00478 
00479 int CAviFile::strm_t::GetFrame(REFERENCE_TIME rt)
00480 {
00481         int frame = -1;
00482 
00483         rt /= 10000; // avoiding overflow later
00484 
00485         if(strh.fccType == FCC('auds'))
00486         {
00487                 WAVEFORMATEX* wfe = (WAVEFORMATEX*)strf.GetData();
00488 
00489                 INT64 size = strh.dwScale
00490                         ? ((rt * strh.dwRate + strh.dwScale/2) / strh.dwScale * wfe->nBlockAlign + 1000/2) / 1000
00491                         : 0;
00492 
00493                 for(frame = 0; frame < cs.GetCount(); frame++)
00494                 {
00495                         if(cs[frame].size > size)
00496                         {
00497                                 frame--;
00498                                 break;
00499                         }
00500                 }
00501         }
00502         else
00503         {
00504                 frame = strh.dwScale
00505                         ? (int)(((rt * strh.dwRate + strh.dwScale/2) / strh.dwScale + 1000/2) / 1000)
00506                         : 0;
00507         }
00508 
00509         if(frame >= cs.GetCount())
00510                 frame = cs.GetCount()-1;
00511 
00512         return(frame);
00513 }
00514 
00515 int CAviFile::strm_t::GetKeyFrame(REFERENCE_TIME rt)
00516 {
00517         int i = GetFrame(rt);
00518         for(; i > 0; i--) {if(cs[i].fKeyFrame) break;}
00519         return(i);
00520 }
00521 
00522 DWORD CAviFile::strm_t::GetChunkSize(DWORD size)
00523 {
00524         if(strh.fccType == FCC('auds'))
00525         {
00526                 WORD nBlockAlign = ((WAVEFORMATEX*)strf.GetData())->nBlockAlign;
00527                 size = nBlockAlign
00528                         ? (size + (nBlockAlign-1)) / nBlockAlign * nBlockAlign // round up for nando's vbr hack
00529                         : 0;
00530         }
00531 
00532         return(size);
00533 }
00534 
00535 bool CAviFile::strm_t::IsRawSubtitleStream()
00536 {
00537         return strh.fccType == FCC('txts') && cs.GetCount() == 1;
00538 }

Generated on Tue Dec 13 14:47:10 2005 for guliverkli by  doxygen 1.4.5