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;
}
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? –
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