程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 基於DirectShow非線性編輯DES

基於DirectShow非線性編輯DES

編輯:關於VC++

摘要

本文詳細闡述了基於DirectShow核心框架的非線性編輯的基本原理,並提供 了一個編輯的源代碼,演示如何拼接兩個音視頻文件,實現視頻過渡效果,並預覽。

編譯環境 WindowsXP,VC6.0+sp5,DX9 SDK.

技術原理

DES (DirectShow Editing Services),是一套基於DirectShow核心框架的編程接口。DES的出現,簡化了視頻 編輯任務,彌補了DirectShow對於媒體文件非線性編輯支持的先天性不足。但是,就技術本 身而言,DES並沒有超越DirectShow Filter架構,而只是DirectShow Filter的一種增強應用 。我們可以從下圖中了解到DES在我們整個多媒體處理應用中的位置。

下面,我們舉個 例子來看一下DES能夠給我們帶來些什麼。假如我們現在有三個文件A、B和C,使用這三個文 件做成一個合成的文件。我們想取A的4秒鐘的內容,緊接著取B的10秒鐘的內容,再緊接著C 的5秒鐘的內容。如果僅僅是這樣,我們直接使用DirectShow Filter是不難實現的。(一般 情況下,應用程序級會維持各個文件的編輯信息,由應用程序根據這些信息動態創建/控制功 能單一的Filter Graph,以順序對各個文件進行處理。)但是,如果我們的"創意 "是隨時改變的,我們現在想讓C在B之前出現,或者我們想取A的不同位置的10秒鐘內容 ,或者我們想給整個合成的文件加上一段美妙的背景音樂。如果我們仍然直接使用 DirectShow Filter去實現,情況就變得很復雜了。然而,對於DES,這真的是小Case!(將所 有的編輯信息以DES提供的接口告訴DES,其它的如Filter Graph的創建/控制輸出,就完全交 給DES來負責吧!這時候,DES創建的Filter Graph帶有各個Source輸出的控制功能,一般比 較復雜。)

如果我們使用DES,我們還可以得到如下的便利:

1. 基於時間線 (Timeline)的結構以及Track的概念,使得多媒體文件的組織、編輯變得直觀而高效;

2. 支持即時的預覽;

3. 視頻編輯項目支持XML文檔的形式保存;

4. 支持對視頻/音頻的效果處理,以及視頻之間切換的過渡處理;

5. 可以直接使用DES 提供的100多種SMPTE過渡效果,以及MS IE自帶的各種Transform、Transition組件;

6. 支持通過色調、亮度、RGB值或者alpha值進行圖像的合成;

7. 自動對源 文件輸出的視頻幀率、音頻的采樣率進行調整,直接支持視頻的縮放。

接下去,我們 來看一下DES的結構(Timeline模型),如下圖所示:

這是一個樹形結 構。在這棵樹中,音視頻文件是葉結點,稱作為Source;一個或多個Source組成一個Track, 每個Track都有統一的媒體格式輸出;Track的集合稱作為Composition,每個Composition可 以對其所有的Composition或Track進行各種復雜的編輯;頂級的Composition或Track就組成 了Group;每個Group輸出單一格式的媒體流,所有的Group組成一個Timeline, Timeline表示 一個視頻編輯的項目,它是這棵樹的根節點。一個Timeline項目必須至少包含一個Group,最 典型的情況一般包含兩個Group:Audio Group和Video Group。

下面,我們來看一個 典型的基於Timeline的Source Track編排。如下圖:

圖中,箭頭方向 即是Timeline的方向。這個Timeline由兩個Group組成,每個Group中包含兩個Source Track 。在Group中,Track是有優先級的(Track 0具有最低的優先級,依次類推)。運行時,總是 輸出高優先級的Track中的Source內容。如果此時高優先級的Track中沒有Source輸出,則讓 低優先級的Track中的Source輸出。如上圖中Video Group的輸出順序為Source A->Source C->Source B。而對於Audio Group,它的所有Track的輸出只是簡單的合成。

我們 再看一個典型的Track之間加入了Transition的Timeline結構。如下圖:

圖中,Video Group中是兩個Track以及Track上幾個Source的編排;Rendered video中表示這個Group最終 輸出的效果。我們可以看到,在Track 1上有一個Transition,表示這個時間段上從Track 0 過渡到Track1的效果。一般,Transition位於高優先級的Track上。Transition也是有方向的 ,默認是從低優先級的Track過渡到高優先級的Track。當然,我們也可以改變Transition的 方向。如下圖所示,第一個Transition是從Track 0到Track 1,第二個Transition是從Track 1到Track 0。

值得注意的是,DES使用的Transition采用了叫做DirectX Transform Object的技術。 任何兩輸入一輸出的DirectX Transform Object都可以用作Transition。遺憾的是,微軟現 在的DirectX SDK不再支持這種組件的開發。我們能夠使用的,只有DES本身提供的幾種效果 ,還有就是Microsoft Internet Explorer自帶的效果。DES使用的Effect情況類似,只不過 DES Effect是單輸入單輸出的DirectX Transform Object。

講到這裡,我們已經對 DES結構有了一個初步的了解。我們需要回過去再看一看這個Timeline樹結構。我們會發現, Group下面一般都有一個Composition,而隨後的圖例中,我們看到一般Group下直接嵌入的是 Track。那麼,Composition有什麼用呢?熟悉《設計模式》的人很容易就明白了,微軟采用 的就是對象結構型模式的其中一種叫Composite(組合)的模式。Composition可以包裝幾個 Track(這幾個Track之間可能是包含Transition的),組成一個Virtual Track,並且與其他 普通的Track接口保持一致。我們完全可以把這個Virtual Track與普通的Track一樣操作,進 而很方便地進行更加復雜、豐富的效果編輯

我提供了一個把兩個.wmv文件進行編輯的 源代碼,有兩個Track,為了簡便,只是提供了視頻的Transition,音頻的Transition也是同 樣的道理,只不過需要多建一個Audio Group。程序編譯需要安裝DX9 SDK。目前上載系統有 問題,無法上傳源代碼,以後補上。以下只主要源代碼。

鏈接需要strmiids.lib庫;

void CDesTestDlg::OnStart()
{
  // 創建空時間線.
   IAMTimeline  *pTL = NULL;
  CoInitialize(NULL);
   CoCreateInstance(CLSID_AMTimeline, NULL, CLSCTX_INPROC_SERVER, IID_IAMTimeline, (void**)&pTL);
  // GROUP: Add a video group to the timeline.
   IAMTimelineGroup  *pGroup = NULL;
  IAMTimelineObj   *pGroupObj = NULL;
  pTL->CreateEmptyNode(&pGroupObj, TIMELINE_MAJOR_TYPE_GROUP);
  pGroupObj->QueryInterface (IID_IAMTimelineGroup, (void **)&pGroup);
  // Set the group media type. This example sets the type to "video" and
  // lets DES pick the default settings. For a more detailed example,
  // see "Setting the Group Media Type."
  AM_MEDIA_TYPE mtGroup;
   ZeroMemory(&mtGroup, sizeof(AM_MEDIA_TYPE));
  mtGroup.majortype = MEDIATYPE_Video;
  pGroup->SetMediaType(&mtGroup);
  pTL- >AddGroup(pGroupObj);
  pGroupObj->Release();
  // TRACK: Add two track to the group.
  IAMTimelineObj   *pTrackObj1,*pTrackObj2;
  IAMTimelineTrack  *pTrack1,*pTrack2;
  IAMTimelineComp   *pComp1 = NULL;//,*pComp2 = NULL;
  pTL->CreateEmptyNode(&pTrackObj1, TIMELINE_MAJOR_TYPE_TRACK);
  pGroup->QueryInterface (IID_IAMTimelineComp, (void **)&pComp1);
  pComp1->VTrackInsBefore (pTrackObj1, -1);
  pTrackObj1->QueryInterface(IID_IAMTimelineTrack, (void **)&pTrack1);
  pTL->CreateEmptyNode(&pTrackObj2, TIMELINE_MAJOR_TYPE_TRACK);
  pGroup->QueryInterface (IID_IAMTimelineComp, (void **)&pComp1);
  pComp1->VTrackInsBefore (pTrackObj2, -1);
  pTrackObj2->QueryInterface(IID_IAMTimelineTrack, (void **)&pTrack2);
  pTrackObj1->Release();
  pTrackObj2- >Release();
  pComp1->Release();
  pGroup->Release();
  // SOURCE: Add two source to the track.
  IAMTimelineSrc *pSource1 = NULL,*pSource2 = NULL;
  IAMTimelineObj *pSourceObj1,*pSourceObj2;
   pTL->CreateEmptyNode(&pSourceObj1, TIMELINE_MAJOR_TYPE_SOURCE);
   pSourceObj1->QueryInterface(IID_IAMTimelineSrc, (void **)&pSource1);
  pTL->CreateEmptyNode(&pSourceObj2, TIMELINE_MAJOR_TYPE_SOURCE);
  pSourceObj2->QueryInterface(IID_IAMTimelineSrc, (void **) &pSource2);
  // Set the times and the file name.
   pSourceObj1->SetStartStop(0, 100000000);
  pSourceObj2- >SetStartStop(50000000, 100000000);
  BSTR bstrFile1 = SysAllocString (OLESTR("news.WMV"));
  BSTR bstrFile2 = SysAllocString(OLESTR ("vos.wmv"));
  pSource1->SetMediaName(bstrFile1);
   pSource2->SetMediaName(bstrFile2);
  SysFreeString(bstrFile1);
   SysFreeString(bstrFile2);
  //設置基於媒體本身的開始和結束時間
   pSource1->SetMediaTimes(00000000, 100000000);
  pSource2- >SetMediaTimes(50000000, 100000000);
  pTrack1->SrcAdd (pSourceObj1);
  pTrack2->SrcAdd(pSourceObj2);
  pSourceObj1- >Release();
  pSourceObj2->Release();
  pSource1->Release ();
  pSource2->Release();
  pTrack1->Release();
   pTrack2->Release();

  // Create the transition object.
   IAMTimelineObj *pTransObj = NULL;
  HRESULT hr = pTL->CreateEmptyNode (&pTransObj, TIMELINE_MAJOR_TYPE_TRANSITION);

  // Set the subobject.
  hr = pTransObj->SetSubObjectGUID(CLSID_DxtJpeg); // SMPTE Wipe

  // Set the start and stop times.
  hr = pTransObj- >SetStartStop(50000000, 100000000);

  // Insert the transition object into the timeline.
  IAMTimelineTransable *pTransable = NULL;
  hr = pTrack2->QueryInterface(IID_IAMTimelineTransable, (void **) &pTransable);
  hr = pTransable->TransAdd(pTransObj);

   IPropertySetter   *pProp;  // Property setter

  hr = CoCreateInstance(CLSID_PropertySetter, NULL, CLSCTX_INPROC_SERVER,
     IID_IPropertySetter, (void**) &pProp);

  // Error checking is omitted for clarity...

  DEXTER_PARAM param;
  DEXTER_VALUE *pValue = (DEXTER_VALUE*)CoTaskMemAlloc(sizeof(DEXTER_VALUE));

  // Initialize the parameter.
  param.Name = SysAllocString (L"MaskNum");
  param.dispID = 0;
  param.nValues = 1;

  // Initialize the value.
  pValue->v.vt = VT_BSTR;
   pValue->v.bstrVal =SysAllocString(L"129"); //六角星
   pValue->rt = 0;
  pValue->dwInterp = DEXTERF_JUMP;

   pProp->AddProp(param, pValue);

  // Free allocated resources.
  SysFreeString(param.Name);
  VariantClear(&(pValue->v));
  CoTaskMemFree(pValue);

  // Set the property on the transition.
  pTransObj->SetPropertySetter(pProp);
  pProp- >Release();

  pTransable->Release();
  pTransObj- >Release();

  // Preview the timeline.
  IRenderEngine *pRenderEngine = NULL;
  CoCreateInstance(CLSID_RenderEngine, NULL, CLSCTX_INPROC_SERVER,
    IID_IRenderEngine, (void**) &pRenderEngine);
  PreviewTL(pTL, pRenderEngine);

  // Clean up.
  pRenderEngine->ScrapIt();
  pRenderEngine- >Release();
  pTL->Release();
  CoUninitialize();
}
// 預覽時間線.
void PreviewTL(IAMTimeline *pTL, IRenderEngine *pRender)
{
  IGraphBuilder  *pGraph = NULL;
  IMediaControl  *pControl = NULL;
  IMediaEvent   *pEvent = NULL;
  // Build the graph.
  pRender->SetTimelineObject(pTL);
  pRender->ConnectFrontEnd( );
  pRender->RenderOutputPins( );
  // Run the graph.
   pRender->GetFilterGraph(&pGraph);
  pGraph->QueryInterface (IID_IMediaControl, (void **)&pControl);
  pGraph->QueryInterface (IID_IMediaEvent, (void **)&pEvent);
  pControl->Run();
   long evCode;
  pEvent->WaitForCompletion(INFINITE, &evCode);
  pControl->Stop();
  // Clean up.
  pEvent->Release();
  pControl->Release();
  pGraph->Release();
}

最後, 希望認識一些對MPEG-4感興趣的同仁,互相學習和交流。

下載源代碼:http://www.vckbase.com/code/downcode.asp?id=2681

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved