Beispiel #1
0
Status MovieExporter::convertToGif( QString ffmpeg, QString strIn, QString strOut )
{
    // http://superuser.com/questions/556029/
    // generate a palette
    QString strGifPalette = mTempWorkDir + "/palette.png";
    QString strCmd1 = QString( "\"%1\"" ).arg( ffmpeg );
    strCmd1 += " -y";
    strCmd1 += QString( " -i \"%1\"" ).arg( strIn );
    strCmd1 += " -vf scale=320:-1:flags=lanczos,palettegen";
    strCmd1 += QString( " \"%1\"" ).arg( strGifPalette );

    STATUS_CHECK( executeFFMpegCommand( strCmd1 ) );

    // Output the GIF using the palette:
    QString strCmd2 = QString( "\"%1\"" ).arg( ffmpeg );
    strCmd2 += " -y";
    strCmd2 += QString( " -i \"%1\"" ).arg( strIn );
    strCmd2 += QString( " -i \"%1\"" ).arg( strGifPalette );
    strCmd2 += " -filter_complex \"scale=-1:-1:flags=lanczos[x];[x][1:v]paletteuse\"";
    strCmd2 += QString( " \"%1\"" ).arg( strOut );

    STATUS_CHECK( executeFFMpegCommand( strCmd2 ) );

    return Status::OK;
}
Beispiel #2
0
Status MovieExporter::combineVideoAndAudio( QString ffmpegPath, QString strOutputFile )
{
    if ( mCanceled )
    {
        return Status::CANCELED;
    }

    //int exportFps = mDesc.videoFps;
    const QString imgPath = mTempWorkDir + IMAGE_FILENAME;
    const QString tempAudioPath = mTempWorkDir + "/tmpaudio.wav";
    const QSize exportSize = mDesc.exportSize;

    QString strCmd = QString("\"%1\"").arg( ffmpegPath );
    strCmd += QString( " -f image2");
    strCmd += QString( " -framerate %1" ).arg( mDesc.fps );
    strCmd += QString( " -pix_fmt yuv420p" );
    strCmd += QString( " -start_number %1" ).arg( mDesc.startFrame );
    //strCmd += QString( " -r %1" ).arg( exportFps );
    strCmd += QString( " -i \"%1\" " ).arg( imgPath );

    if ( QFile::exists( tempAudioPath ) )
    {
        strCmd += QString( " -i \"%1\" " ).arg( tempAudioPath );
    }

    strCmd += QString( " -s %1x%2" ).arg( exportSize.width() ).arg( exportSize.height() );
    strCmd += " -y";
    strCmd += QString(" \"%1\"" ).arg( strOutputFile );

    STATUS_CHECK( executeFFMpegCommand( strCmd ) );

    return Status::OK;
}
/**
 *  OpenGL display function.
 *
 *  This function is called every 25 milliseconds by the update
 *  function.
 */
void glutDisplay (void)
{
    /**
     *  Update every node of OpenNI.
     */
    g_Context.WaitOneUpdateAll(g_DepthGenerator);

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(40.0, 1.05, 1.0, 10000.0);
    gluLookAt(320.0, -300.0, 4200.0,
              320.0, 240.0, 1500.0,
              0.0,-1.0, 0.0);

    glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();

    glPushMatrix();

    // Checking fot game starting and finishing
    g_SFBgame.checkUsers();

    if (g_SFBgame.isGameOn()) {
        
        g_UserDetector.changeStopDetection(true);
        g_SFBgame.checkGameOver();

        if (!g_SFBgame.isGameOver()) {
            g_BusterDetector -> detectPose();
            g_IceRodDetector -> detectPose();
        } 
        else {
            STATUS_CHECK(g_Context.StopGeneratingAll(), 
                "Context generation shutdown");
        }
    }
    else {
        // Detects poses
        g_ZamusDetector -> detectPose();
        g_LinqDetector -> detectPose();
    }
    
   
    /**
     *  Use the draw functions of every class to display the game with
     *  OpenGL.
     */
    g_SceneRenderer.drawScene();
    g_SFBgame.drawFireBalls();
    g_SFBgame.drawGameInfo();
    g_SFBgame.nextFrame();

    glPopMatrix();
    glutSwapBuffers();
}
Beispiel #4
0
Status MovieExporter::twoPassEncoding( QString ffmpeg, QString strOutputFile )
{
    QString strTempVideo = mTempWorkDir + "/Temp1.mp4";
    qDebug() << "TempVideo=" << strTempVideo;

    combineVideoAndAudio( ffmpeg, strTempVideo );

    if ( strOutputFile.endsWith( "gif" ) )
    {
        STATUS_CHECK( convertToGif( ffmpeg, strTempVideo, strOutputFile ) );
    }
    else
    {
        STATUS_CHECK( convertVideoAgain( ffmpeg, strTempVideo, strOutputFile ) );
    }

    return Status::OK;
}
Beispiel #5
0
Status MovieExporter::run(const Object* obj,
                          const ExportMovieDesc& desc,
                          std::function<void( float )> progress )
{
    progress( 0.f );

    QString ffmpegPath = ffmpegLocation();
    qDebug() << ffmpegPath;
    if ( !QFile::exists( ffmpegPath ) )
    {
        qDebug() << "Please place ffmpeg.exe in " << ffmpegPath << " directory";
        return Status::ERROR_FFMPEG_NOT_FOUND;
    }

    STATUS_CHECK( checkInputParameters( desc ) );
    mDesc = desc;

    qDebug() << "OutFile: " << mDesc.strFileName;

    // Setup temporary folder
    if ( !mTempDir.isValid() )
    {
        Q_ASSERT( false && "Cannot create temp folder." );
        return Status::FAIL;
    }

    mTempWorkDir = mTempDir.path();
    progress( 0.03f );

    if ( !desc.strFileName.endsWith( "gif" ) )
    {
        STATUS_CHECK( assembleAudio( obj, ffmpegPath, progress ) );
    }
    progress( 0.10f );

    STATUS_CHECK( generateImageSequence( obj, progress ) );
    progress( 0.99f );

    twoPassEncoding( ffmpegPath, desc.strFileName );

    progress( 1.0f );

    return Status::OK;
}
Beispiel #6
0
Status MovieExporter::convertVideoAgain( QString ffmpegPath, QString strIn, QString strOut )
{
    QString strCmd = QString("\"%1\"").arg( ffmpegPath );
    strCmd += QString( " -i \"%1\" " ).arg( strIn );
    strCmd += QString( " -pix_fmt yuv420p" );
    strCmd += " -y";
    strCmd += QString(" \"%1\"" ).arg( strOut );

    STATUS_CHECK( executeFFMpegCommand( strCmd ) );
    return Status::OK;
}
Beispiel #7
0
int
http_status (int fd, int no)
{
	http_code *c;
	char buf[128];
	int rc;

	STATUS_CHECK(no);
	STATUS_ASSIGN(no,c);

	sprintf (buf, HTTP_PROTOCOL" %d %s\r\n", c->num, c->str);
	rc = write (fd, buf, strlen(buf));
	return (rc);
}
/**
 * Initialize XN functions
 */
void initialize() 
{
    ImageMetaData imageMD;
    XnStatus status;
    int dummy;
    
    srand ( time(NULL) );

    // Initializing context and checking for enumeration errors
    status = g_Context.InitFromXmlFile(XML_CONFIG_FILE, &g_Error);
    checkEnumError(status, g_Error);

    // Finding nodes and checking for errors
    STATUS_CHECK(g_Context.FindExistingNode(XN_NODE_TYPE_DEPTH, g_DepthGenerator), "Finding depth node");
    STATUS_CHECK(g_Context.FindExistingNode(XN_NODE_TYPE_SCENE, g_SceneAnalyzer), "Finding scene analizer");


    //  Note: when the image generation node is handled the program gets
    //  too slow.

    // STATUS_CHECK(g_Context.FindExistingNode(XN_NODE_TYPE_IMAGE, g_ImageGenerator), "Finding image node");

    // Set view point of Depth generator to the image generator point of
    // view.
    // STATUS_CHECK(g_DepthGenerator.GetAlternativeViewPointCap().SetViewPoint(g_ImageGenerator), "Set View Point");

    STATUS_CHECK(g_Context.FindExistingNode(XN_NODE_TYPE_USER, g_UserGenerator), "Finding user node");
    
    //g_ImageGenerator.GetMetaData(imageMD);
    
    // Checking camera pixel format
    //if (imageMD.PixelFormat() != XN_PIXEL_FORMAT_RGB24) {
    //    reportError("Camera format not supported...!\n");
    //}

    // Checking user generator capabilities
    if(!g_UserGenerator.IsCapabilitySupported(XN_CAPABILITY_SKELETON)) {
        reportError("Skeleton capability not supported\n");
    }
    
    if(!g_UserGenerator.IsCapabilitySupported(XN_CAPABILITY_POSE_DETECTION)) {
        reportError("Pose detection capability not supported\n");
    }

    printf("Number of players: ");
    dummy = scanf("%d", &g_MaxPlayers);
    printf("\n");

    //Initialize user detector object
    g_UserDetector = UserDetector(g_UserGenerator, g_DepthGenerator);
    g_UserDetector.registerCallbacks();
    
    g_ZamusDetector = new Zamus(&g_UserDetector);
    g_LinqDetector = new Linq(&g_UserDetector);
    g_BusterDetector = new BusterDetector(g_ZamusDetector, &g_UserDetector);
    g_IceRodDetector = new IceRodDetector(g_LinqDetector, &g_UserDetector);

    // Initialize image render object
    g_SceneRenderer = SceneRenderer(&g_ImageGenerator,
                                    &g_DepthGenerator,
                                    &g_SceneAnalyzer,
                                    &g_UserDetector,
                                    g_ZamusDetector,
                                    g_LinqDetector);

    STATUS_CHECK(g_Context.StartGeneratingAll(), "Context generation");

    g_SFBgame = SuperFiremanBrothers(&g_UserDetector, 
                                     &g_SceneAnalyzer, 
                                     g_ZamusDetector, 
                                     g_LinqDetector, 
                                     g_MaxPlayers
                                    );

}
Beispiel #9
0
void TemplateDispatch<T>::dispatchKernel(T run_args, hsa_signal_t& signal,
	const Launch_params_t lparm) {
hsa_dispatch_packet_t run_Aql;
HSAContextKaveriImpl::KernelImpl *p_impl =
		(HSAContextKaveriImpl::KernelImpl *) m_p_kernel;

hsa_status_t err;
status_t status = STATUS_SUCCESS;
/*  Create a signal to wait for the dispatch to finish.  */
err = hsa_signal_create(1, 0, NULL, &signal);
STATUS_CHECK(err, __LINE__);

/*  Setup this call to this kernel dispatch packet from scratch.  */
memset(&run_Aql, 0, sizeof(run_Aql));
run_Aql.completion_signal = signal;

/*  Set the dimensions passed from the application */
run_Aql.dimensions = (uint16_t) lparm.ndim;
run_Aql.grid_size_x = lparm.gdims[0];
run_Aql.workgroup_size_x = lparm.ldims[0];
if (lparm.ndim > 1) {
	run_Aql.grid_size_y = lparm.gdims[1];
	run_Aql.workgroup_size_y = lparm.ldims[1];
} else {
	run_Aql.grid_size_y = 1;
	run_Aql.workgroup_size_y = 1;
}
if (lparm.ndim > 2) {
	run_Aql.grid_size_z = lparm.gdims[2];
	run_Aql.workgroup_size_z = lparm.ldims[2];
} else {
	run_Aql.grid_size_z = 1;
	run_Aql.workgroup_size_z = 1;
}

/*  In the future, we may use environment variables for some of these */
run_Aql.header.type = HSA_PACKET_TYPE_DISPATCH;
run_Aql.header.acquire_fence_scope = 2;
run_Aql.header.release_fence_scope = 2;
run_Aql.header.barrier = 1;
run_Aql.group_segment_size =
		p_impl->hsaCodeDescriptor->workgroup_group_segment_byte_size;
run_Aql.private_segment_size =
		p_impl->hsaCodeDescriptor->workitem_private_segment_byte_size;

/*  copy args from the custom run_args structure */
/*  FIXME We should align kernel_arg_buffer because run_args is aligned */
memcpy(run_kernel_arg_buffer, &run_args, sizeof(run_args));

/*  Bind kernelcode to the packet.  */
run_Aql.kernel_object_address = p_impl->hsaCodeDescriptor->code.handle;

/*  Bind kernel argument buffer to the aql packet.  */
run_Aql.kernarg_address = (uint64_t) run_kernel_arg_buffer;

/*  Obtain the current queue write index. increases with each call to kernel  */
uint64_t index = hsa_queue_load_write_index_relaxed(
		p_impl->context->commandQueue);
/* printf("DEBUG:Call #%d to kernel \"%s\" \n",(int) index+1,"run"); */

/*  Write the run_Aql packet at the calculated queue index address.  */
const uint32_t queueMask = p_impl->context->commandQueue->size - 1;
const uint32_t pos = index & queueMask;
((hsa_dispatch_packet_t*) (p_impl->context->commandQueue->base_address))[pos] =
		run_Aql;

/* Increment the write index and ring the doorbell to dispatch the kernel.  */
hsa_queue_store_write_index_relaxed(p_impl->context->commandQueue, index + 1);

hsa_signal_store_relaxed(p_impl->context->commandQueue->doorbell_signal, index);
return;
}
Beispiel #10
0
/** Begin exporting the movie described by exportDesc.
 *
 * @param[in] obj An Object containing the animation to export.
 * @param[in] desc A structure containing all the export parameters.
 *            See ExportMovieDesc.
 * @param[out] majorProgress A function to update the major progress bar.
 *                          The major progress bar goes from 0-100% only
 *                          one time, representing the overall progress of
 *                          the export. The first float parameter is
 *                          the current progress %, and the second is
 *                          the desired progress when the next sub-task
 *                          completes. This should only be called at the
 *                          beginning of a subtask.
 * @param[out] minorProgress A function to update the minor progress bar.
 *                           The minor progress bar goes from 0-100% for
 *                           each sub-task of the exporting process.
 *                           It is up to minor progress to update the
 *                           major progress bar to reflect the sub-task
 *                           progress.
 * @param[out] progressMessage A function ot update the progres bar
 *                             message. The messages will describe
 *                             the current sub-task of the exporting
 *                             process.
 *
 * @return Returns Status:OK on success, or Status::FAIL on error.
 */
Status MovieExporter::run(const Object* obj,
                          const ExportMovieDesc& desc,
                          std::function<void(float, float)> majorProgress,
                          std::function<void(float)> minorProgress,
                          std::function<void(QString)> progressMessage)
{
    majorProgress(0.f, 0.03f);
    minorProgress(0.f);
    progressMessage(QObject::tr("Checking environment..."));

    clock_t t1 = clock();

    QString ffmpegPath = ffmpegLocation();
    qDebug() << ffmpegPath;
    if (!QFile::exists(ffmpegPath))
    {
#ifdef _WIN32
        qCritical() << "Please place ffmpeg.exe in " << ffmpegPath << " directory";
#else
        qCritical() << "Please place ffmpeg in " << ffmpegPath << " directory";
#endif
        return Status::ERROR_FFMPEG_NOT_FOUND;
    }

    STATUS_CHECK(checkInputParameters(desc));
    mDesc = desc;

    qDebug() << "OutFile: " << mDesc.strFileName;

    // Setup temporary folder
    if (!mTempDir.isValid())
    {
        Q_ASSERT(false && "Cannot create temp folder.");
        return Status::FAIL;
    }

    mTempWorkDir = mTempDir.path();

    minorProgress(0.f);
    if (desc.strFileName.endsWith("gif", Qt::CaseInsensitive))
    {
        majorProgress(0.03f, 1.f);
        progressMessage("Generating gif...");
        minorProgress(0.f);
        STATUS_CHECK(generateGif(obj, ffmpegPath, desc.strFileName, minorProgress));
    }
    else
    {
        majorProgress(0.03f, 0.25f);
        progressMessage("Assembling audio...");
        minorProgress(0.f);
        STATUS_CHECK(assembleAudio(obj, ffmpegPath, minorProgress));
        minorProgress(1.f);
        majorProgress(0.25f, 1.f);
        progressMessage("Generating movie...");
        STATUS_CHECK(generateMovie(obj, ffmpegPath, desc.strFileName, minorProgress));
    }
    minorProgress(1.f);
    majorProgress(1.f, 1.f);
    progressMessage(QObject::tr("Done"));
    
    clock_t t2 = clock() - t1;
    qDebug("MOVIE = %.1f sec", static_cast<double>(t2 / CLOCKS_PER_SEC));

    return Status::OK;
}
Beispiel #11
0
/** Exports obj to a gif image at strOut using FFmpeg.
 *
 *  @param[in]  obj An Object containing the animation to export.
 *  @param[in]  ffmpegPath The path to the FFmpeg binary.
 *  @param[in]  strOut The output path. Should end with .gif.
 *  @param[out] progress A function that takes one float argument
 *              (the percentage of the gif generation complete) and
 *              may display the output to the user in any way it
 *              sees fit.
 *
 *  @return Returns the final status of the operation (ok or canceled)
 */
Status MovieExporter::generateGif(
        const Object* obj,
        QString ffmpegPath,
        QString strOut,
        std::function<void(float)> progress)
{

    if (mCanceled)
    {
        return Status::CANCELED;
    }

    // Frame generation setup

    int frameStart = mDesc.startFrame;
    int frameEnd = mDesc.endFrame;
    const QSize exportSize = mDesc.exportSize;
    bool transparency = false;
    QString strCameraName = mDesc.strCameraName;
    bool loop = mDesc.loop;
    int bytesWritten;

    auto cameraLayer = static_cast<LayerCamera*>(obj->findLayerByName(strCameraName, Layer::CAMERA));
    if (cameraLayer == nullptr)
    {
        cameraLayer = obj->getLayersByType< LayerCamera >().front();
    }
    int currentFrame = frameStart;

    /* We create an image with the correct dimensions and background
     * color here and then copy this and draw over top of it to
     * generate each frame. This is faster than having to generate
     * a new background image for each frame.
     */
    QImage imageToExportBase(exportSize, QImage::Format_ARGB32_Premultiplied);
    QColor bgColor = Qt::white;
    if (transparency)
    {
        bgColor.setAlpha(0);
    }
    imageToExportBase.fill(bgColor);

    QSize camSize = cameraLayer->getViewSize();
    QTransform centralizeCamera;
    centralizeCamera.translate(camSize.width() / 2, camSize.height() / 2);

    // Build FFmpeg command

    QString strCmd = QString("\"%1\"").arg(ffmpegPath);
    strCmd += QString(" -f rawvideo -pixel_format bgra");
    strCmd += QString(" -video_size %1x%2").arg(exportSize.width()).arg(exportSize.height());
    strCmd += QString(" -framerate %1").arg(mDesc.fps);

    strCmd += " -i -";

    strCmd += " -y";

    strCmd += " -filter_complex \"[0:v]palettegen [p]; [0:v][p] paletteuse\"";

    strCmd += QString(" -loop %1").arg(loop ? "0" : "-1");
    strCmd += QString(" \"%1\"").arg(strOut);

    // Run FFmpeg command

    STATUS_CHECK(executeFFMpegPipe(strCmd, progress, [&](QProcess& ffmpeg, int framesProcessed)
    {
        /* The GIF FFmpeg command requires the entires stream to be
         * written before FFmpeg can encode the GIF. This is because
         * the generated pallete is based off of the colors in all
         * frames. The only way to avoid this would be to generate
         * all the frames twice and run two separate commands, which
         * would likely have unacceptable speed costs.
         */

        Q_UNUSED(framesProcessed);
        if(currentFrame > frameEnd)
        {
            ffmpeg.closeWriteChannel();
            return false;
        }

        QImage imageToExport = imageToExportBase.copy();
        QPainter painter(&imageToExport);

        QTransform view = cameraLayer->getViewAtFrame(currentFrame);
        painter.setWorldTransform(view * centralizeCamera);
        painter.setWindow(QRect(0, 0, camSize.width(), camSize.height()));

        obj->paintImage(painter, currentFrame, false, true);

        bytesWritten = ffmpeg.write(reinterpret_cast<const char*>(imageToExport.constBits()), imageToExport.byteCount());
        Q_ASSERT(bytesWritten == imageToExport.byteCount());

        currentFrame++;

        return true;
    }));

    return Status::OK;
}
Beispiel #12
0
/** Exports obj to a movie image at strOut using FFmpeg.
 *
 *  @param[in]  obj An Object containing the animation to export.
 *  @param[in]  ffmpegPath The path to the FFmpeg binary.
 *  @param[in]  strOutputFile The output path. Should end with .gif.
 *  @param[out] progress A function that takes one float argument
 *              (the percentage of the gif generation complete) and
 *              may display the output to the user in any way it
 *              sees fit.
 *
 *  The movie formats supported by this operation are any file
 *  formats that the referenced FFmpeg binary supports and that have
 *  the required features (ex. video and audio support)
 *
 *  @return Returns the final status of the operation (ok or canceled)
 */
Status MovieExporter::generateMovie(
        const Object* obj,
        QString ffmpegPath,
        QString strOutputFile,
        std::function<void(float)> progress)
{
    if (mCanceled)
    {
        return Status::CANCELED;
    }

    // Frame generation setup

    int frameStart = mDesc.startFrame;
    int frameEnd = mDesc.endFrame;
    const QSize exportSize = mDesc.exportSize;
    bool transparency = mDesc.alpha;
    QString strCameraName = mDesc.strCameraName;
    bool loop = mDesc.loop;

    auto cameraLayer = static_cast<LayerCamera*>(obj->findLayerByName(strCameraName, Layer::CAMERA));
    if (cameraLayer == nullptr)
    {
        cameraLayer = obj->getLayersByType< LayerCamera >().front();
    }
    int currentFrame = frameStart;

    /* We create an image with the correct dimensions and background
     * color here and then copy this and draw over top of it to
     * generate each frame. This is faster than having to generate
     * a new background image for each frame.
     */
    QImage imageToExportBase(exportSize, QImage::Format_ARGB32_Premultiplied);
    QColor bgColor = Qt::white;
    if (transparency)
    {
        bgColor.setAlpha(0);
    }
    imageToExportBase.fill(bgColor);

    QSize camSize = cameraLayer->getViewSize();
    QTransform centralizeCamera;
    centralizeCamera.translate(camSize.width() / 2, camSize.height() / 2);

    int failCounter = 0;
    /* Movie export uses a "sliding window" to reduce memory usage
     * while having a relatively small impact on speed. This basically
     * means that there is a maximum number of frames that can be waiting
     * to be encoded by ffmpeg at any one time. The limit is set by the
     * frameWindow variable which is designed to take up a maximum of
     * about 1GB of memory
     */
    int frameWindow = static_cast<int>(1e9 / (camSize.width() * camSize.height() * 4.0));

    // Build FFmpeg command

    //int exportFps = mDesc.videoFps;
    const QString tempAudioPath = mTempWorkDir + "/tmpaudio.wav";

    QString strCmd = QString("\"%1\"").arg(ffmpegPath);
    strCmd += QString(" -f rawvideo -pixel_format bgra");
    strCmd += QString(" -video_size %1x%2").arg(exportSize.width()).arg(exportSize.height());
    strCmd += QString(" -framerate %1").arg(mDesc.fps);

    //strCmd += QString( " -r %1").arg( exportFps );
    strCmd += QString(" -i -");
    strCmd += QString(" -threads %1").arg(QThread::idealThreadCount() == 1 ? 0 : QThread::idealThreadCount());

    if (QFile::exists(tempAudioPath))
    {
        strCmd += QString(" -i \"%1\" ").arg(tempAudioPath);
    }

    if (strOutputFile.endsWith(".apng", Qt::CaseInsensitive))
    {
        strCmd += QString(" -plays %1").arg(loop ? "0" : "1");
    }

    if (strOutputFile.endsWith("mp4", Qt::CaseInsensitive))
    {
        strCmd += QString(" -pix_fmt yuv420p");
    }

    if (strOutputFile.endsWith(".avi", Qt::CaseInsensitive))
    {
        strCmd += " -q:v 5";
    }

    strCmd += " -y";
    strCmd += QString(" \"%1\"").arg(strOutputFile);

    // Run FFmpeg command

    STATUS_CHECK(executeFFMpegPipe(strCmd, progress, [&](QProcess& ffmpeg, int framesProcessed)
    {
        if(framesProcessed < 0)
        {
            failCounter++;
        }

        if(currentFrame > frameEnd)
        {
            ffmpeg.closeWriteChannel();
            return false;
        }

        if((currentFrame - frameStart <= framesProcessed + frameWindow || failCounter > 10) && currentFrame <= frameEnd)
        {
            QImage imageToExport = imageToExportBase.copy();
            QPainter painter(&imageToExport);

            QTransform view = cameraLayer->getViewAtFrame(currentFrame);
            painter.setWorldTransform(view * centralizeCamera);
            painter.setWindow(QRect(0, 0, camSize.width(), camSize.height()));

            obj->paintImage(painter, currentFrame, false, true);
            painter.end();

            // Should use sizeInBytes instead of byteCount to support large images,
            // but this is only supported in QT 5.10+
            int bytesWritten = ffmpeg.write(reinterpret_cast<const char*>(imageToExport.constBits()), imageToExport.byteCount());
            Q_ASSERT(bytesWritten == imageToExport.byteCount());

            currentFrame++;
            failCounter = 0;
            return true;
        }

        return false;
    }));

    return Status::OK;
}
Beispiel #13
0
/** Combines all audio tracks in obj into a single file.
 *
 *  @param[in] obj
 *  @param[in] ffmpegPath
 *  @param[out] progress A function that takes one float argument
 *              (the percentage of the audio assembly complete) and
 *              may display the output to the user in any way it
 *              sees fit.
 *
 *  @return Returns the final status of the operation. Ok if successful,
 *          or safe if there was intentionally no output.
 */
Status MovieExporter::assembleAudio(const Object* obj,
                                    QString ffmpegPath,
                                    std::function<void(float)> progress)
{
    // Quicktime assemble call
    const int startFrame = mDesc.startFrame;
    const int endFrame = mDesc.endFrame;
    const int fps = mDesc.fps;

    Q_ASSERT(startFrame >= 0);
    Q_ASSERT(endFrame >= startFrame);

    QDir dir(mTempWorkDir);
    Q_ASSERT(dir.exists());

    QString tempAudioPath = mTempWorkDir + "/tmpaudio0.wav";
    qDebug() << "TempAudio=" << tempAudioPath;

    std::vector< SoundClip* > allSoundClips;

    std::vector< LayerSound* > allSoundLayers = obj->getLayersByType<LayerSound>();
    for (LayerSound* layer : allSoundLayers)
    {
        layer->foreachKeyFrame([&allSoundClips](KeyFrame* key)
        {
            allSoundClips.push_back(static_cast<SoundClip*>(key));
        });
    }

    int clipCount = 0;

    QString strCmd, filterComplex, amergeInput, panChannelLayout;
    strCmd += QString("\"%1\"").arg(ffmpegPath);

    int wholeLen = qCeil((endFrame - startFrame) * 44100.0 / fps);
    for (auto clip : allSoundClips)
    {
        if (mCanceled)
        {
            return Status::CANCELED;
        }

        // Add sound file as input
        strCmd += QString(" -i \"%1\"").arg(clip->fileName());

        // Offset the sound to its correct position
        // See https://superuser.com/questions/716320/ffmpeg-placing-audio-at-specific-location
        filterComplex += QString("[%1:a:0] aformat=sample_fmts=fltp:sample_rates=44100:channel_layouts=mono,volume=1,adelay=%2S|%2S,apad=whole_len=%3[ad%1];")
                    .arg(clipCount).arg(qRound(44100.0 * (clip->pos() - 1) / fps)).arg(wholeLen);
        amergeInput += QString("[ad%1]").arg(clipCount);
        panChannelLayout += QString("c%1+").arg(clipCount);

        clipCount++;
    }
    // Remove final '+'
    panChannelLayout.chop(1);
    // Output arguments
    // Mix audio
    strCmd += QString(" -filter_complex \"%1%2 amerge=inputs=%3, pan=mono|c0=%4 [out]\"")
            .arg(filterComplex).arg(amergeInput).arg(clipCount).arg(panChannelLayout);
    // Convert audio file: 44100Hz sampling rate, stereo, signed 16 bit little endian
    // Supported audio file types: wav, mp3, ogg... ( all file types supported by ffmpeg )
    strCmd += " -ar 44100 -acodec pcm_s16le -ac 2 -map \"[out]\" -y";
    // Trim audio
    strCmd += QString(" -ss %1").arg((startFrame - 1) / static_cast<double>(fps));
    strCmd += QString(" -to %1").arg(endFrame / static_cast<double>(fps));
    // Output path
    strCmd += " " + mTempWorkDir + "/tmpaudio.wav";

    STATUS_CHECK(executeFFMpeg(strCmd, progress));
    qDebug() << "audio file: " + tempAudioPath;

    return Status::OK;
}