Esempio n. 1
0
/**
Write a metadata file alongside the media files.
This is maintained for compatibility with an earlier version of the recorder
but is not ideal as this file can become inconsistent with the "true" metadata.
Can perhaps be used to determine when the recorded files are complete.
*/
bool IngexRecorder::WriteMetadataFile(const char * meta_name)
{
    FILE *fp_meta = NULL;
    char tmp_meta_name[FILENAME_MAX];
    strcpy(tmp_meta_name, meta_name);
    strcat(tmp_meta_name, ".tmp");

    if ((fp_meta = fopen(tmp_meta_name, "wb")) == NULL)
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT("Could not open for write: %C %p\n"), tmp_meta_name, ACE_TEXT("fopen")));
        return false;
    }
    fprintf(fp_meta, "path=%s\n", meta_name);
    //fprintf(fp_meta, "a_tape=");
    //for (int i = 0; i < mChannels; i++)
    //{
    //  fprintf(fp_meta, "%s%s", record_opt[i].tapename.c_str(), i == MAX_CHANNELS - 1 ? "\n" : ",");
    //}
    fprintf(fp_meta, "enabled=");
    for (int i = 0; i < MAX_RECORD; i++)
    {
        fprintf(fp_meta, "%d%s", record_opt[i].enabled, i == MAX_RECORD - 1 ? "\n" : ",");
    }
    fprintf(fp_meta, "start_tc=%d\n", mStartTimecode);
    fprintf(fp_meta, "start_tc_str=%s\n", Timecode(mStartTimecode, mFps, mDf).Text());
    fprintf(fp_meta, "capture_length=%d\n", record_opt[0].FramesWritten());
#if 0
    fprintf(fp_meta, "a_start_tc=");
    for (int i = 0; i < mChannels + 1; i++)
    {
        fprintf(fp_meta, "%d%s", record_opt[i].start_tc, i == mChannels ? "\n" : " ");
    }
    fprintf(fp_meta, "a_start_tc_str=");
    for (int i = 0; i < mChannels + 1; i++)
    {
        fprintf(fp_meta, "%s%s", framesToStr(record_opt[i].start_tc, tmpstr), i == mChannels ? "\n" : " ");
    }
    fprintf(fp_meta, "a_duration=");
    for (int i = 0; i < mChannels + 1; i++)
    {
        fprintf(fp_meta, "%d%s", record_opt[i].FramesWritten(), i == mChannels ? "\n" : " ");
    }
    fprintf(fp_meta, "description=%s\n", ""); // not necessarily known at this time
#endif
    fclose(fp_meta);

    if (rename(tmp_meta_name, meta_name) != 0)
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT( "Could not rename %s to %s %p\n"), tmp_meta_name, meta_name, ACE_TEXT("rename")));
        perror("rename");
        return false;
    }

    return true;
}
Esempio n. 2
0
/** 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;
}
Esempio n. 3
0
/**
Prepare for a recording.  Search for target timecode and set some of the RecordOptions.
This should be called before IngexRecorder::Setup().
*/
bool IngexRecorder::CheckStartTimecode(
                //std::vector<bool> & channel_enables,
                const std::vector<bool> & track_enables,
                framecount_t & start_timecode,
                framecount_t pre_roll,
                bool crash_record)
{
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("IngexRecorder::CheckStartTimecode()\n")));

    // Set up channel enables
#if 1
    for (unsigned int i = 0; mpImpl && i < mpImpl->mTracks->length() && i < track_enables.size(); ++i)
    {
        ProdAuto::Track & track = mpImpl->mTracks->operator[](i);
        HardwareTrack hw_trk = mpImpl->mTrackMap[track.id];
        

        if (track.has_source && track_enables[i])
        {
            mChannelEnable[hw_trk.channel] = true;
        }
    }
#else
    mChannelEnable = channel_enables;
#endif

    // Store track enables for use by recorder_functions
    mTrackEnable = track_enables;

    unsigned int n_channels = IngexShm::Instance()->Channels();

    framecount_t target_tc;

    // If crash record, search across all channels for the minimum current (lastframe) timecode
    if (crash_record)
    {
        //int tc[MAX_CHANNELS];
        struct { int framecount; bool valid; } tc[MAX_CHANNELS];
        for (unsigned int i = 0; i < MAX_CHANNELS; ++i)
        {
            tc[i].valid = false;
        }

        ACE_DEBUG((LM_DEBUG, ACE_TEXT("Crash record:\n")));
        for (unsigned int channel_i = 0; channel_i < mChannelEnable.size(); channel_i++)
        {
            if (mChannelEnable[channel_i] && IngexShm::Instance()->SignalPresent(channel_i))
            {
                tc[channel_i].framecount = IngexShm::Instance()->CurrentTimecode(channel_i);
                tc[channel_i].valid = true;

                ACE_DEBUG((LM_DEBUG, ACE_TEXT("    tc[%d]=%C\n"),
                    channel_i, Timecode(tc[channel_i].framecount, mFps, mDf).Text()));
            }
        }

        int max_tc = 0;
        int min_tc = INT_MAX;
        bool tc_valid = false;
        for (unsigned int channel_i = 0; channel_i < n_channels; channel_i++)
        {
            if (tc[channel_i].valid)
            {
                max_tc = max(max_tc, tc[channel_i].framecount);
                min_tc = min(min_tc, tc[channel_i].framecount);
                tc_valid = true;
            }
        }

        if (!tc_valid)
        {
            ACE_DEBUG((LM_ERROR, ACE_TEXT("    No channels enabled!\n")));
        }

        target_tc = min_tc;
        //target_tc = tc[1]; // just for testing
    }
    else
    {
        target_tc = start_timecode;
    }

    // Include pre-roll
    if (target_tc < pre_roll)
    {
        target_tc += 24 * 60 * 60 * 25;
    }
    target_tc -= pre_roll;

    // NB. Should be keeping target_tc and pre-roll separate in case of discontinuous timecode.
    // i.e. find target_tc and then step back by pre-roll.

    // Start timecode for record threads.
    // Also used for setting stop duration (not the ideal way to do it).
    mStartTimecode = target_tc;

    // Passing back the actual start_timecode.
    start_timecode = target_tc;

    // local vars
    bool found_all_target = true;
    unsigned int search_limit;
    const int ring_length = IngexShm::Instance()->RingLength();
    if (ring_length > SEARCH_GUARD)
    {
        search_limit = ring_length - SEARCH_GUARD;
    }
    else
    {
        search_limit = 1;
    }

    // Search for desired timecode across all target sources
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("Searching for timecode %C\n"), Timecode(target_tc, mFps, mDf).Text()));
    for (unsigned int channel_i = 0; channel_i < n_channels; channel_i++)
    {
        if (! mChannelEnable[channel_i])
        {
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("Channel %d not enabled\n"), channel_i));
            continue;
        }

        int lastframe = IngexShm::Instance()->LastFrame(channel_i);

        bool found_target = false;
        int first_tc_seen = 0;
        int last_tc_seen = 0;

        // Find target timecode.
        for (unsigned int i = 0; !found_target && i < search_limit; ++i)
        {
            // read timecode value
            int frame = lastframe - i;
            int tc = IngexShm::Instance()->Timecode(channel_i, frame);

            if (i == 0)
            {
                first_tc_seen = tc;
            }
            last_tc_seen = tc;

            if (tc == target_tc)
            {
                mStartFrame[channel_i] = lastframe - i;
                found_target = true;

                ACE_DEBUG((LM_DEBUG, ACE_TEXT("Found channel%d lf=%6d lf-i=%8d tc=%C\n"),
                    channel_i, lastframe, lastframe - i, Timecode(tc, mFps, mDf).Text()));
            }
            else if (i == 0 && target_tc > tc && target_tc - tc < 5)
            {
                // Target is slightly in the future.  We predict the start frame.
                mStartFrame[channel_i] = frame + target_tc - tc;
                found_target = true;

                ACE_DEBUG((LM_WARNING, ACE_TEXT("Target timecode in future for channel[%d]: target=%C, most_recent=%C\n"),
                    channel_i,
                    Timecode(target_tc, mFps, mDf).Text(),
                    Timecode(first_tc_seen, mFps, mDf).Text()
                    ));
            }
        }

        if (! found_target)
        {
            ACE_DEBUG((LM_ERROR, "channel[%d] Target tc %C not found, buffer %C - %C\n",
                channel_i,
                Timecode(target_tc, mFps, mDf).Text(),
                Timecode(last_tc_seen, mFps, mDf).Text(),
                Timecode(first_tc_seen, mFps, mDf).Text()
            ));

            if (IngexShm::Instance()->SignalPresent(channel_i))
            {
                found_all_target = false;
            }
            else
            {
                // As there is no signal at this input we won't
                // prevent recording from starting but will
                // simply disable this input.
                ACE_DEBUG((LM_ERROR,
                    ACE_TEXT("This channel has no signal present so will be disabled for the current recording.\n")
                    ));
                mChannelEnable[channel_i] = false;
            }
        }
    } // for channel_i


    // Return
    if (!found_all_target)
    {
        ACE_DEBUG((LM_ERROR, ACE_TEXT("Could not start record - not all target timecodes found\n")));
        return false;
    }
    else
    {
        //ACE_DEBUG((LM_DEBUG, ACE_TEXT("IngexRecorder::PrepareStart() returning true\n")));
        return true;
    }
}
Esempio n. 4
0
bool IngexRecorder::Stop( framecount_t & stop_timecode,
                framecount_t post_roll,
                const char * description,
                const std::vector<Locator> & locs)
{
    if (stop_timecode < 0)
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("IngexRecorder::Stop(\"now\")\n")));
    }
    else
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("IngexRecorder::Stop(%C, %d)\n"),
            Timecode(stop_timecode, mFps, mDf).Text(), post_roll));
    }

    // Store description
    if (description)
    {
        mUserComments.push_back(
            prodauto::UserComment(AVID_UC_DESCRIPTION_NAME, description, STATIC_COMMENT_POSITION, 0));
    }

    // Store locators
    for (std::vector<Locator>::const_iterator it = locs.begin(); it != locs.end(); ++it)
    {
        int64_t position = it->timecode - mStartTimecode;
        mUserComments.push_back(
            prodauto::UserComment(POSITIONED_COMMENT_NAME, it->comment, position, it->colour));
    }

    // Really want to pass stop_timecode and post_roll to the record threads
    // or do some kind of "prepare_stop"
    // but for now will set an absolute duration.

    //post_roll = 0;  //tmp test

    framecount_t capture_length = 0;
    if (stop_timecode < 0)
    {
        // Stop timecode is "now".
        // Find longest current recorded duration and add post_roll.

        for (std::vector<ThreadParam>::iterator
            it = mThreadParams.begin(); it != mThreadParams.end(); ++it)
        {
            framecount_t frames_written = it->p_opt->FramesWritten();
            framecount_t frames_dropped = it->p_opt->FramesDropped();
            framecount_t frames_total = frames_written + frames_dropped;

            ACE_DEBUG((LM_DEBUG, ACE_TEXT("%C frames total = %d (written = %d, dropped = %d)\n"),
                SOURCE_NAME[it->p_opt->channel_num], frames_total, frames_written, frames_dropped));

            if (capture_length < frames_total)
            {
                capture_length = frames_total;
            }
        }
        capture_length += post_roll;
    }
    else
    {
        // Calculate capture length.
        capture_length = stop_timecode - mStartTimecode + post_roll;
    }

    // Return the expected "out time" of the recording
    stop_timecode = mStartTimecode + capture_length;

    // guard against crazy capture lengths
    if (capture_length <= 0)
    {
        // force immediate-ish finish of recording
        // true duration will be updated from frames_written variable
        ACE_DEBUG((LM_ERROR, ACE_TEXT("recorder_stop: crazy duration of %d passed - stopping recording immediately by\n"
                "              signalling duration of 1.\n"), capture_length));
        capture_length = 1;
    }

    // Now stop record by signalling the duration in frames
    TargetDuration(capture_length);
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("Target duration set to %d\n"), capture_length));


    return true;
}
Esempio n. 5
0
int32_t IngexShm::CurrentTimecode(unsigned int channel)
{
    // Could check (channel < mChannels) first.
    return Timecode(channel, LastFrame(channel));
}