最近在更新公司一款Premiere Pro CC導入插件的時候,遇到了一個神奇的現象。具體的現象是這樣的:我們的插件需要將一些私有的文件數據放到插件中,比如說當前活動的文件名。當插件中收到不同的selector時,我們能夠隨時獲取到這些私有數據進行操作。具體來說,我們是在收到imGetPrefs8這個selector時,進行設置的。回調函數代碼如下:
static prMALError
SDKGetPrefs8(
imStdParms *stdParms,
imFileAccessRec8 *fileInfo8,
imGetPrefsRec *prefsRec)
{
//-----------------
// The first time you are called (or if you've been Quieted or Closed)
// you will get asked for prefs data. First time called, return the
// size of the buffer you want Premiere to store prefs for your plug-in.
// Note: if canOpen is not set to kPrTrue, I'm not getting this selector. Why?
// Answer: because this selector is associated directly with "hasSetup"
if (prefsRec->prefsLength == 0) {
prefsRec->prefsLength = sizeof(MediaSettings);
}
else {
MediaSettings* settings = (MediaSettings*)prefsRec->prefs;
//do not show dialog for the first time.
if (fileInfo8->fileref != imInvalidHandleValue) {
auto ctx = (FileContext*)(fileInfo8->fileref);
if (!ctx || !ctx->media_source) {
return malNoError;
}
auto oldSettings = ctx->media_source->GetMediaSettings();
settings->layout = oldSettings.layout;
settings->lock_direction = oldSettings.lock_direction;
settings->use_flowstate = oldSettings.use_flowstate;
settings->media_case = oldSettings.media_case;
settings->need_update = !settings->need_update;
std::string currentFile = ctx->media_source->GetFilePath();
ctx->media_source->ShowSettingsDialog(settings, currentFile);
updateSettingFromFile(settings);
ctx->media_source->UpdateSettings(settings);
}
else {
//init settings
settings->use_flowstate = true;
}
}
return malNoError;
}
根據Adobe官方提供的文檔說明,imGetPrefs8這個selector在文件導入的時候會連續發送兩次。第一次調用回調函數是為了獲取用戶私有數據緩存區的大小,Host程序會給我們分配這么大的一塊緩沖區。第二次調用的時候,這塊緩沖區已經分配好了。我們只要往這塊內存寫入私有用戶數據就行了。按道理說,這個過程非常清晰明了,不應該出現什么問題。可是在我實際調試的時候,彈窗獲取到用戶輸入后,並沒有馬上生效!那么,我是怎么判斷用戶輸入之后沒有生效呢?一般來說,如果用戶更改了什么設置,那么需要插件立即去調用SDKGetSourceVideo()函數的。
static prMALError SDKGetSourceVideo( imStdParms *stdparms, imFileRef fileRef, imSourceVideoRec *sourceVideoRec) { int ret; ImporterLocalRec8H ldataH = reinterpret_cast<ImporterLocalRec8H>(sourceVideoRec->inPrivateData); ImporterLocalRec8Ptr localRecP = reinterpret_cast<ImporterLocalRec8Ptr>(*ldataH); // Get parameters for ReadFrameToBuffer() imFrameFormat* frameFormat = &sourceVideoRec->inFrameFormats[0]; prRect theRect; if (frameFormat->inFrameWidth == 0 && frameFormat->inFrameHeight == 0) { frameFormat->inFrameWidth = localRecP->theFile.width; frameFormat->inFrameHeight = localRecP->theFile.height; } // Windows and MacOS have different definitions of Rects, so use the cross-platform prSetRect prSetRect(&theRect, 0, 0, frameFormat->inFrameWidth, frameFormat->inFrameHeight); localRecP->PPixCreatorSuite->CreatePPix(sourceVideoRec->outFrame, PrPPixBufferAccess_ReadWrite, frameFormat->inPixelFormat, &theRect); csSDK_int32 theFrame = static_cast<csSDK_int32>(sourceVideoRec->inFrameTime / (*ldataH)->theFile.frameRate); FileContext* ctx = (FileContext*)(localRecP)->fileRef; if (ctx == nullptr) { return imNoContent; } ... ... return imNoErr; }
這個函數負責根據當用的用戶設置來重新生成一幀數據傳遞給Host程序渲染,這樣用戶才能實時看到設置生效了。問題是,我們的用戶輸入改變之后,SDKGetSourceVideo()這個方法並沒有再次調用!那是什么原因導致的呢?難道是Premiere主程序有什么Bug?經過不斷調試才發現,這個鍋Premiere不能背啊!原因是,我在imGetPrefs8的回調函數中並沒有修改私有數據。也就是說,在上面的SDKGetPrefs8()方法中,我通過prefsRec獲取到用戶私有數據緩沖區之后,如果沒有修改過這塊內存區的數據的話,Premiere會認為不需要重新渲染畫面,也就不會再次調用SDKGetSourceVideo()方法了。
MediaSettings* settings = (MediaSettings*)prefsRec->prefs;
這么看來,Premiere的這個機制還是有道理的。如果用戶私有數據沒有更改,很大可能是不需要重新渲染畫面的。這在某些計算頻繁的場景下可能能夠提供一定的性能提升。但是在文檔里面並沒有注明這一點。所以實際上,這個現象看起來像是一個鍋實際上並不是一個鍋……
