void _mp4v2_read_chapters(MP4V2Handles *handle) { VALUE self = handle->self; MP4Chapter_t *chaps = NULL; uint32_t count; MP4GetChapters(handle->file, &chaps, &count, MP4ChapterTypeAny); VALUE chapters = rb_ary_new2(count), chapter; MP4Duration sum = 0; for (uint32_t i = 0; i < count; i++) { chapter = rb_funcall(rb_cChapter, rb_intern("new"), 2, DBL2NUM(sum), rb_utf8_str(chaps[i].title)); rb_ary_push(chapters, chapter); sum += chaps[i].duration; } free(chaps); handle->chapters = NULL; SET(chapters, chapters); }
/** Action for exporting chapters from the <b>job.file</b> * * * @param job the job to process * @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise */ bool ChapterUtility::actionExport( JobContext& job ) { job.fileHandle = MP4Read( job.file.c_str() ); if( job.fileHandle == MP4_INVALID_FILE_HANDLE ) { return herrf( "unable to open for read: %s\n", job.file.c_str() ); } // get the list of chapters MP4Chapter_t* chapters = 0; uint32_t chapterCount = 0; MP4ChapterType chtp = MP4GetChapters( job.fileHandle, &chapters, &chapterCount, _ChapterType ); if (0 == chapterCount) { return herrf( "File \"%s\" does not contain chapters of type %s\n", job.file.c_str(), getChapterTypeName( chtp ).c_str() ); } // build the filename string outName = job.file; if( _ChapterFile.empty() ) { FileSystem::pathnameStripExtension( outName ); outName.append( ".chapters.txt" ); } else { outName = _ChapterFile; } ostringstream oss; oss << "Exporting " << chapterCount << " " << getChapterTypeName( chtp ); oss << " chapters from file " << '"' << job.file << '"' << " into chapter file " << '"' << outName << '"' << endl; verbose1f( "%s", oss.str().c_str() ); if( dryrunAbort() ) { // free up the memory MP4Free(chapters); return SUCCESS; } // open the file File out( outName, File::MODE_CREATE ); if( openFileForWriting( out ) ) { // free up the memory MP4Free(chapters); return FAILURE; } // write the chapters #if defined( _WIN32 ) static const char* LINEND = "\r\n"; #else static const char* LINEND = "\n"; #endif File::Size nout; bool failure = SUCCESS; int width = 2; if( CHPT_FMT_COMMON == _ChapterFormat && (chapterCount / 100) >= 1 ) { width = 3; } Timecode duration( 0, CHAPTERTIMESCALE ); duration.setFormat( Timecode::DECIMAL ); for( uint32_t i = 0; i < chapterCount; ++i ) { // print the infos ostringstream oss; switch( _ChapterFormat ) { case CHPT_FMT_COMMON: oss << "CHAPTER" << setw( width ) << setfill( '0' ) << i+1 << '=' << duration.svalue << LINEND << "CHAPTER" << setw( width ) << setfill( '0' ) << i+1 << "NAME=" << chapters[i].title << LINEND; break; case CHPT_FMT_NATIVE: default: oss << duration.svalue << ' ' << chapters[i].title << LINEND; } string str = oss.str(); if( out.write( str.c_str(), str.size(), nout ) ) { failure = herrf( "write to %s failed: %s\n", outName.c_str(), sys::getLastErrorStr() ); break; } // add the duration of this chapter to the sum (the start time of the next chapter) duration += Timecode(chapters[i].duration, CHAPTERTIMESCALE); } out.close(); if( failure ) { verbose1f( "removing file %s\n", outName.c_str() ); ::remove( outName.c_str() ); } // free up the memory MP4Free(chapters); return SUCCESS; }