Support This Site | Has this site helped, informed, or amused you? Please support my work and ongoing site improvements in one of these ways: |
Use your credit card or PayPal to donate in support of the site. | |
Use this link to Amazon—you pay the same, I get 4%. | |
Learn Thai with my Talking Thai-English-Thai Dictionary app: iOS, Android, Windows. | |
Experience Thailand richly with my Talking Thai-English-Thai Phrasebook app. | |
Visit China easily with my Talking Chinese-English-Chinese Phrasebook app. | |
I co-authored this bilingual cultural guide to Thai-Western romantic relationships. | |
Submit This Site | Like what you see? Help spread the word on social media: | |||
| ||||
Note: Updated Lurker's Guide available (but not this page!)This page belongs to the old 1990s SGI Lurker's Guide. As of 2008, several of the Lurker's Guide pages have been updated for HDTV and for modern OS platforms like Windows and Mac. This particular page is not one of those, but you can see what new stuff is available here. Thanks!
This document has the following goals:
If you choose to do the latter, keep in mind that the movie library will require space for its first QuickTime atom at the beginning of the file as well as extending the file with the remainder of the QuickTime header. The minimum empty space required at the beginning of the file is 128 bytes (assuming that you will want to take advantage of the movie library's ability to generate QuickTime files with 64 bit file offsets). In practice, however, most often 1K or 4K (or some other convenient size, perhaps derived from the disk block size) is more commonly used (extra space is simply ignored).
Do not call mvInsertTrackDataAtOffset with an offset less than the minimum headersize of 128. If you do, when the movie header is written to disk(during mvClose for instance) the header will overwrite the first few bytes of the first block of data. This will appear to the user as a few bad pixels in the first field/frame. Also remember mvInsertTrackDataAtOffset does not move the file pointer for you. This means to leave space for the header you must call a function like lseek with your initial offset before your first write.
If you do your own file I/O it is important that you return the file pointer to the position it was in when the movie library lasted used it for file I/O. If you do all your own file I/O this often means returning it to position 0 (with a call to lseek) just before calling mvClose which flushes the movie header.
To insert raw buffers of data (eg, perhaps compressed JPEG data that you have received from the dmIC library, or uncompressed data you have received from the VL), you would normally do the following.
To insert data which is already on disk into the movie's edit list (so that its location is stored in the file, and can be played back using standard QuickTime compatible tools), you would normally perform a multiple step process centered around mvInsertTrackDataAtOffset and friends. Note that mvInsertTrackDataAtOffset does not write anything to disk (nor does mvGetTrackDataIndex, nor mvSetTrackDataFieldInfo); it simply updates in memory movie data structures, which will be flushed to disk when mvWrite or mvClose is called.
Also, note that calling mvInsertTrackDataAtOffset need not be called between each write; you can also write all of your data to disk in one pass (and maintain your own data structure keeping track of where each frame/field is on disk), and then have a post-processing step that calls mvInsertTrackDataAtOffset. In some cases, this may be preferable, since mvInsertTrackDataAtOffset may call malloc; however, in other cases, it may be preferable to call it after each write, as this will provide some amount of CPU/IO overlap.
DMstatus initMovie( int fd, int width, int height, MVid *movie, MVid *track) { DMstatus s = DM_FAILURE; DMparams *movieP = NULL; DMparams *trackP = NULL; *movie = NULL; *track = NULL; if (dmParamsCreate(&movieP) != DM_SUCCESS) goto ERR; if (mvSetMovieDefaults(movieP, MV_FORMAT_QT) != DM_SUCCESS) goto ERR; if (mvCreateFD(fd, movieP, NULL, movie) != DM_SUCCESS) goto ERR; if (dmParamsCreate(&trackP) != DM_SUCCESS) goto ERR; if (mvSetImageDefaults(trackP, width, height, MV_FORMAT_QT) != DM_SUCCESS) goto ERR; if (dmParamsSetEnum(trackP, DM_IMAGE_ORIENTATION, DM_IMAGE_TOP_TO_BOTTOM) != DM_SUCCESS) goto ERR; if (dmParamsSetEnum(trackP, DM_IMAGE_INTERLACING, DM_IMAGE_INTERLACED_ODD) != DM_SUCCESS) goto ERR; if (dmParamsSetEnum(trackP, DM_IMAGE_PACKING, DM_IMAGE_PACKING_CbYCrY) != DM_SUCCESS) goto ERR; if (mvAddTrack(*movie, DM_IMAGE, trackP, NULL, track) != DM_SUCCESS) goto ERR; s = DM_SUCCESS; ERR: if (movieP != NULL) dmParamsDestroy(movieP); if (trackP != NULL) dmParamsDestroy(trackP); return s; }
MVtime frameDur = 1001; MVtime TS = 30000; MVtime curTime = 0; // Note that before this loop is entered, you should have already // seeked far enough to leave space for the first QT Atom. while( !done ) { off64_t off1, off2; size64_t sz1, sz2; int index, dummy; void * data1, * data2; // this function writes data to disk, and fills in off1, sz1, // off2, sz2. // it is the thing that actually calls the write system call. // // In its most basic form it could be something along the lines // of the following: // // off1 = tell64( fd ); // write( fd, data1, sz1 ); // off2 = tell64( fd ); // write( fd, data2, sz2 ); // myWriterFunction( &off1, &sz1, &off2, &sz2 ); // Now, tell the movie lib about the data just written to disk. // This assumes that off1 < off2, although that is not strictly // required. s = mvInsertTrackDataAtOffset( track, 1, curTime, frameDur, TS, off1, 0, MV_FRAMETYPE_KEY, 0 ); // Since we will be immediately calling mvSetTrackDataFieldInfo, // which overwrites the size set by mvInsertTrackDataAtOffset, // pass in a zero as the size above. if ( s != DM_SUCCESS ) ERROR(); s = mvGetTrackDataIndexAtTime( track, curTime, TS, &index, &dummy ); if ( s != DM_SUCCESS ) ERROR(); s = mvSetTrackDataFieldInfo( track, index, sz1, off2-(off1+sz1), sz2 ); curTime += frameDur; }
Note that when reading raw data from disk, you must pay attention to whether the chunk of data was written as a frame or as a field pair. Pay special attention the the mvTrackDataHasFieldInfo call below.
Also, it is important to note that the QuickTime file format allows the compression/size/etc parameters of chunks within a track to change at any point within the track. For this reason, you need to use mvGetTrackDataInfo (and possibly mvGetTrackDataParams) to make sure the data for the current chunk is in a format you are expecting. Although it is uncommon to encounter files with varying parameters for chunks within one track, it does happen, and therefore is something that any code reading raw data from a QuickTime file must be aware of. Depending on your application, you may wish to abort when this happens, change codecs/etc, or, simply fall back to the general purpose movielib function mvRenderMovieToImageBuffer or mvRenderMovieToAudioBuffer. The mvRender* functions in the movielib have full support for changing chunk parameters, so they make a good fallback case. The correct behavior will obviously depend upon your application.
Just as with the "writing raw data into a QuickTime file" (example above), you have a choice as to how to read the data: you may either pass the movielib one or two void* buffers (mvReadTrackData/mvReadTrackDataFields), or you may ask the movielib for the on-disk offset of a chunk of data (using mvGetTrackDataInfo/mvGetTrackDataFieldInfo) and perform the read operation yourself. Obviously, the former is simpler for non performance critical applications, but the latter allows you to do things like combine multiple reads into one system call (eg, readv), or use directio, allowing you to (potentially) avoid extra bcopy operations.
Since all but one step is the same for the void* method as for the read method, I've combined them into one description.
Support This Site | Has this site helped, informed, or amused you? Please support my work and ongoing site improvements in one of these ways: |
Use your credit card or PayPal to donate in support of the site. | |
Use this link to Amazon—you pay the same, I get 4%. | |
Learn Thai with my Talking Thai-English-Thai Dictionary app: iOS, Android, Windows. | |
Experience Thailand richly with my Talking Thai-English-Thai Phrasebook app. | |
Visit China easily with my Talking Chinese-English-Chinese Phrasebook app. | |
I co-authored this bilingual cultural guide to Thai-Western romantic relationships. | |
Copyright | All text and images copyright 1999-2023 Chris Pirazzi unless otherwise indicated. |