2015-01-16 14 views
5

Sto cercando di implementare un MFT che è in grado di ruotare un video. La rotazione stessa verrebbe eseguita all'interno di una funzione di trasformazione. Per questo ho bisogno di cambiare la dimensione del frame di output, ma non so come farlo.Media Foundation - Come cambiare la dimensione del frame in MFT (Media Foundation Transform)

Come punto di partenza, ho utilizzato l'esempio MFT_Grayscale fornito da Microsoft. Ho incluso questo MFT in una topologia parziale come trasformazione nodo

HRESULT Player::AddBranchToPartialTopology(
    IMFTopology *pTopology, 
    IMFPresentationDescriptor *pSourcePD, 
    DWORD iStream 
    ) 
{ 
    ... 
    IMFTopologyNode pTransformNode = NULL; 
    ... 
    hr = CreateTransformNode(CLSID_GrayscaleMFT, &pTransformNode); 
    ... 
    hr = pSourceNode->ConnectOutput(0, pTransformNode, 0); 
    hr = pTransformNode->ConnectOutput(0, pOutputNode, 0); 
    ... 
} 

Questo codice funzioni finora. Il mft in scala di grigi viene applicato e funziona come previsto. In ogni caso voglio cambiare questo mft per gestire la rotazione del video. Quindi, supponiamo di voler ruotare un video di 90 gradi. Per questo è necessario cambiare la larghezza e l'altezza del mio frame di input. Ho provato diverse cose ma nessuna di esse funziona come previsto. Sulla base del primo commento in questa discussione How to change Media Foundation Transform output frame(video) size? ho iniziato a modificare l'implementazione di SetOutputType. Ho chiamato GetAttributeSize all'interno di GetOutputType per ricevere l'effettivo frame_size. Viene a mancare quando provo a impostare un nuovo frame_size (quando si avvia la riproduzione ricevo hresult 0xc00d36b4 (dati specificato non è valido, incoerenti o non supportata da questo oggetto)

HRESULT CGrayscale::SetOutputType(
    DWORD   dwOutputStreamID, 
    IMFMediaType *pType, // Can be NULL to clear the output type. 
    DWORD   dwFlags 
    ) 
{ .... 
    //Receive the actual frame_size of pType (works as expected) 
    hr = MFGetAttributeSize(
    pType, 
    MF_MT_FRAME_SIZE, 
    &width, 
    &height 
    )); 
    ... 
    //change the framesize 
    hr = MFSetAttributeSize(
    pType, 
    MF_MT_FRAME_SIZE, 
    height, 
    width 
    )); 
} 

Sono sicuro che mi manca qualcosa qui, in modo che qualsiasi suggerimento sarà molto apprezzato.

Grazie in anticipo

+0

Mi sono imbattuto in questa domanda. Prima di provare a scrivere una risposta, stai ancora cercando una soluzione qui? O l'hai risolto da solo/spostato? –

+0

ciao, grazie per la risposta. anche se in realtà non sto lavorando su questo argomento, sono ancora interessato a come risolvere questo problema. Quindi se sei disposto a scrivere una risposta qui è molto apprezzato :) – sebhaub

risposta

2

C'è una molta fortuna transform disponibile in W8 + che dovrebbe fare la rotazione. non ho avuto con me stesso, ma presumibilmente può essere fatto per il lavoro Presumo che non sia una soluzione praticabile per te.

Il caso più interessante è la creazione di una MFT per eseguire la trasformazione.

Si scopre che esistono diversi passaggi per trasformare "Scala di grigi" in un rotatore.

1) Come avete ipotizzato, è necessario modificare le dimensioni del fotogramma sul tipo di output. Tuttavia, la modifica del tipo passato a SetOutputType è semplicemente sbagliata. Il pType che viene inviato a SetOutputType è il tipo che il client chiede di supportare. Cambiando quel tipo di supporto a qualcosa di altro rispetto a quello che chiedevano, quindi restituendo S_OK per dire che il supporto non ha senso.

Invece quello che è necessario modificare è il valore restituito da GetOutputAvailableType.

2) Quando si calcola il tipo da inviare indietro da GetOutputAvailableType, è necessario basarlo su IMFMediaType del client inviato a SetInputType, con alcune modifiche. E sì, vuoi modificare MF_MT_FRAME_SIZE, ma probabilmente devi anche regolare MF_MT_DEFAULT_STRIDE, MF_MT_GEOMETRIC_APERTURE e (possibilmente) MF_MT_MINIMUM_DISPLAY_APERTURE. In teoria potrebbe essere necessario regolare anche MF_MT_SAMPLE_SIZE.

3) Non hai detto se intendevi fissare l'importo della rotazione all'inizio del flusso o qualcosa che varia durante la riproduzione. Quando ho scritto questo, ho utilizzato IMFAttributes restituito da IMFTransform :: GetAttributes per specificare la rotazione. Prima che ogni frame venga elaborato, viene letto il valore corrente. Per fare in modo che funzioni correttamente, devi essere in grado di inviare MF_E_TRANSFORM_STREAM_CHANGE indietro da OnProcessOutput.

4) Essendo pigro, non volevo capire come ruotare NV12 o YUY2 o alcuni di questi. Ma ci sono funzioni prontamente disponibili per fare questo per RGB32. Quindi, quando viene chiamato il mio GetInputAvailableType, chiedo per RGB32.

Ho sperimentato il supporto di altri tipi di input, come RGB24, RGB565, ecc., Ma ho riscontrato un problema. Quando il tuo tipo di output è RGB24, MF aggiunge un altro MFT downstream per riconvertire il RGB24 in qualcosa che può essere più facilmente utilizzato (possibilmente RGB32). E che MFT non supporta il supporto mid-stream. Sono stato in grado di farlo funzionare accettando la varietà di sottotipi per l'input, ma sempre emettendo RGB32, ruotato come specificato.

Sembra complicato, ma soprattutto non lo è. Se leggi il codice probabilmente vai "Oh, ho capito". Ti offrirei il mio codice sorgente, ma non sono sicuro di quanto sarebbe utile per te. È in C#, e stavi chiedendo di C++.

D'altra parte, sto creando un modello per semplificare la scrittura di documenti MFT. ~ Una dozzina di righe di codice C# per creare la MFT più semplice possibile. L'MFT di rotazione C# è di ~ 131 linee come contato dalle metriche del codice Analisi/Calcolo di VS (escluso il modello). Sto sperimentando una versione C++, ma è ancora un po 'approssimativa.

Ho dimenticato qualcosa? Probabilmente un sacco di cose. Come non dimenticare di generare una nuova guida per la tua MFT invece di usare la scala di grigi. Ma penso di aver raggiunto i punti migliori.

Modifica: Ora che la versione C++ del modello sta iniziando a funzionare, mi sento a mio agio postando del codice reale. Ciò potrebbe rendere più chiari alcuni dei punti sopra. Per esempio in # 2, parlo di basare il tipo di output sul tipo di input. Puoi vedere che succede in CreateOutputFromInput. E il codice di rotazione attuale è in WriteIt().

Ho semplificato un po 'il codice per le dimensioni, ma spero che questo ti porti a "Oh, ho capito".

void OnProcessSample(IMFSample *pSample, bool Discontinuity, int InputMessageNumber) 
{ 
    HRESULT hr = S_OK; 

    int i = MFGetAttributeUINT32(GetAttributes(), AttribRotate, 0); 
    i &= 7; 

    // Will the output use different dimensions than the input? 
    bool IsOdd = (i & 1) == 1; 

    // Does the current AttribRotate rotation give a different 
    // orientation than the old one? 
    if (IsOdd != m_WasOdd) 
    { 
     // Yes, change the output type. 
     OutputSample(NULL, InputMessageNumber); 
     m_WasOdd = IsOdd; 
    } 

    // Process it. 
    DoWork(pSample, (RotateFlipType)i); 

    // Send the modified input sample to the output sample queue. 
    OutputSample(pSample, InputMessageNumber); 
} 

void OnSetInputType() 
{ 
    HRESULT hr = S_OK; 

    m_imageWidthInPixels = 0; 
    m_imageHeightInPixels = 0; 
    m_cbImageSize = 0; 
    m_lInputStride = 0; 

    IMFMediaType *pmt = GetInputType(); 

    // type can be null to clear 
    if (pmt != NULL) 
    { 
     hr = MFGetAttributeSize(pmt, MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels); 
     ThrowExceptionForHR(hr); 

     hr = pmt->GetUINT32(MF_MT_DEFAULT_STRIDE, &m_lInputStride); 
     ThrowExceptionForHR(hr); 

     // Calculate the image size (not including padding) 
     m_cbImageSize = m_imageHeightInPixels * m_lInputStride; 
    } 
    else 
    { 
     // Since the input must be set before the output, nulling the 
     // input must also clear the output. Note that nulling the 
     // input is only valid if we are not actively streaming. 

     SetOutputType(NULL); 
    } 
} 

IMFMediaType *CreateOutputFromInput(IMFMediaType *inType) 
{ 
    // For some MFTs, the output type is the same as the input type. 
    // However, since we are rotating, several attributes in the 
    // media type (like frame size) must be different on our output. 
    // This routine generates the appropriate output type for the 
    // current input type, given the current state of m_WasOdd. 

    IMFMediaType *pOutputType = CloneMediaType(inType); 

    if (m_WasOdd) 
    { 
     HRESULT hr; 
     UINT32 h, w; 

     // Intentionally backward 
     hr = MFGetAttributeSize(inType, MF_MT_FRAME_SIZE, &h, &w); 
     ThrowExceptionForHR(hr); 

     hr = MFSetAttributeSize(pOutputType, MF_MT_FRAME_SIZE, w, h); 
     ThrowExceptionForHR(hr); 

     MFVideoArea *a = GetArea(inType, MF_MT_GEOMETRIC_APERTURE); 
     if (a != NULL) 
     { 
      a->Area.cy = h; 
      a->Area.cx = w; 
      SetArea(pOutputType, MF_MT_GEOMETRIC_APERTURE, a); 
     } 

     a = GetArea(inType, MF_MT_MINIMUM_DISPLAY_APERTURE); 
     if (a != NULL) 
     { 
      a->Area.cy = h; 
      a->Area.cx = w; 
      SetArea(pOutputType, MF_MT_MINIMUM_DISPLAY_APERTURE, a); 
     } 

     hr = pOutputType->SetUINT32(MF_MT_DEFAULT_STRIDE, w * 4); 
     ThrowExceptionForHR(hr); 
    } 

    return pOutputType; 
} 

void WriteIt(BYTE *pBuffer, RotateFlipType fm) 
{ 
    Bitmap *v = new Bitmap((int)m_imageWidthInPixels, (int)m_imageHeightInPixels, (int)m_lInputStride, PixelFormat32bppRGB, pBuffer); 
    if (v == NULL) 
     throw (HRESULT)E_OUTOFMEMORY; 

    try 
    { 
     Status s; 

     s = v->RotateFlip(fm); 
     if (s != Ok) 
      throw (HRESULT)E_UNEXPECTED; 

     Rect r; 

     if (!m_WasOdd) 
     { 
      r.Width = (int)m_imageWidthInPixels; 
      r.Height = (int)m_imageHeightInPixels; 
     } 
     else 
     { 
      r.Height = (int)m_imageWidthInPixels; 
      r.Width = (int)m_imageHeightInPixels; 
     } 

     BitmapData bmd; 
     bmd.Width = r.Width, 
     bmd.Height = r.Height, 
     bmd.Stride = 4*bmd.Width; 
     bmd.PixelFormat = PixelFormat32bppARGB; 
     bmd.Scan0 = (VOID*)pBuffer; 
     bmd.Reserved = NULL; 

     s = v->LockBits(&r, ImageLockModeRead + ImageLockModeUserInputBuf, PixelFormat32bppRGB, &bmd); 
     if (s != Ok) 
      throw (HRESULT)E_UNEXPECTED; 

     s = v->UnlockBits(&bmd); 
     if (s != Ok) 
      throw (HRESULT)E_UNEXPECTED; 
    } 
    catch(...) 
    { 
     delete v; 
     throw; 
    } 

    delete v; 
} 
+0

Grazie per il tuo impegno nello scrivere una così bella risposta! Sono anche interessato al modello di cui stai parlando. È disponibile ovunque online? – sebhaub

+0

Anche se non mi sembra che sia ancora abbastanza "fatto", potrei usare qualche feedback in questo momento. Eri interessato al modello C++ o C#? –

+0

Non hai mai detto se volevi le versioni C++ o C#. Entrambi sono ora pronti in versione beta su [http://www.LimeGreenSocks.com/MFT](http://www.LimeGreenSocks.com/MFT). Feedback benvenuto –

Problemi correlati