Bonjour,
Je travaille sur de l acquisition WDM (source webcam ou autre) avec directX9 et je cherche a créer un fichier AVI qui contient le résultat de la capture.
Je me base sur un exemple fourni dans la doc directx9 (playcap, dvapp, et amcap).
Cependant, meme si j ai l impression de bien suivre la procédure, et que par le biais de status, on me confirme dans un fichier de debug :
Ou une ligne correspond a une tentative de capture, ce qui donc est encourageant puisque la capture s opere bien.
Captured 57 frames in 2.2 sec (0 dropped): 25.4 fps 5.855 Meg/sec
Captured 58 frames in 2.4 sec (0 dropped): 24.0 fps 5.540 Meg/sec
Captured 118 frames in 4.1 sec (0 dropped): 28.1 fps 6.482 Meg/sec
Captured 118 frames in 4.2 sec (0 dropped): 27.6 fps 6.362 Meg/sec
Captured 118 frames in 4.1 sec (0 dropped): 28.2 fps 6.508 Meg/sec
Captured 118 frames in 4.2 sec (0 dropped): 27.8 fps 6.409 Meg/sec
Captured 149 frames in 5.2 sec (0 dropped): 28.5 fps 6.585 Meg/sec
Captured 149 frames in 6.7 sec (0 dropped): 22.1 fps 5.111 Meg/sec
Captured 148 frames in 5.1 sec (0 dropped): 28.4 fps 6.561 Meg/sec
Captured 148 frames in 5.1 sec (1 dropped): 28.4 fps 6.563 Meg/sec
Captured 88 frames in 3.2 sec (0 dropped): 26.9 fps 6.209 Meg/sec
Captured 87 frames in 3.2 sec (0 dropped): 26.6 fps 6.139 Meg/sec
Le fichier AVI créé ne contient rien.
pourtant j alloue bien la taille de mon fichier:
Je construis bien mon graph de capture
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 // pre-allocate the capture file // BOOL AllocCaptureFile(HWND hWnd) { USES_CONVERSION; // we'll get into an infinite loop in the dlg proc setting a value if(gcap.szCaptureFile[0] == 0) return FALSE; /* * show the allocate file space dialog to encourage * the user to pre-allocate space */ BOOL f = MakeBuilder(); if(!f) return FALSE; if(gcap.pBuilder->AllocCapFile(T2W(gcap.szCaptureFile), (DWORDLONG)gcap.wCapFileSize * 1024L * 1024L) != NOERROR) { MessageBox(hWnd, TEXT("Error"), TEXT("Failed to pre-allocate capture file space"), MB_OK | MB_ICONEXCLAMATION); return FALSE; } return TRUE; }
Comme on peut le voir ci dessus, le fichier est bien paramétré pour l'AVI (je cherche pas a gérer le MPEG, ce qui simplifie un peu le code et je gere par l audio ce qui simplifie aussi).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195 // build the capture graph // File name is defined in gcap.szCaptureFile BOOL CVideoWnd::BuildCaptureGraph2(CString imagFichier) { USES_CONVERSION; HRESULT hr; BOOL f; AM_MEDIA_TYPE *pmt=0; if(gcap.fCaptureGraphBuilt) return TRUE; if(gcap.fCapturing || gcap.fPreviewing)// we're running return FALSE; if(gcap.pVCap == NULL)// We don't have the necessary capture filters return FALSE; // if(gcap.pACap == NULL && gcap.fCapAudio) return FALSE; // no capture file name yet... we need one first if(gcap.szCaptureFile[0] == 0) { f = SetCaptureFile2(m_hWnd,imagFichier); if(!f) return 0; } // we already have another graph built... tear down the old one if(gcap.fPreviewGraphBuilt) TearDownGraph(); // // We need a rendering section that will write the capture file out in AVI // file format // GUID guid = MEDIASUBTYPE_Avi; hr = gcap.pBuilder->SetOutputFileName(&guid, T2W(gcap.szCaptureFile), &gcap.pRender, &gcap.pSink); if(hr != NOERROR) { // ErrMsg(TEXT("Cannot set output file")); goto SetupCaptureFail; } // Now tell the AVIMUX to write out AVI files that old apps can read properly. // If we don't, most apps won't be able to tell where the keyframes are, // slowing down editing considerably // Doing this will cause one seek (over the area the index will go) when // you capture past 1 Gig, but that's no big deal. // NOTE: This is on by default, so it's not necessary to turn it on // Also, set the proper MASTER STREAM hr = gcap.pRender->QueryInterface(IID_IConfigAviMux, (void **)&gcap.pConfigAviMux); if(hr == NOERROR && gcap.pConfigAviMux) { gcap.pConfigAviMux->SetOutputCompatibilityIndex(FALSE); } // // Render the video capture and preview pins - even if the capture filter only // has a capture pin (and no preview pin) this should work... because the // capture graph builder will use a smart tee filter to provide both capture // and preview. We don't have to worry. It will just work. // // NOTE that we try to render the interleaved pin before the video pin, because // if BOTH exist, it's a DV filter and the only way to get the audio is to use // the interleaved pin. Using the Video pin on a DV filter is only useful if // you don't want the audio. hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, gcap.pVCap, NULL, gcap.pRender); if(hr != NOERROR) { hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, gcap.pVCap, NULL, gcap.pRender); if(hr != NOERROR) { // ErrMsg(TEXT("Cannot render video capture stream")); goto SetupCaptureFail; } } if(gcap.fWantPreview) { hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Interleaved, gcap.pVCap, NULL, NULL); if(hr == VFW_S_NOPREVIEWPIN) { // preview was faked up for us using the (only) capture pin gcap.fPreviewFaked = TRUE; } else if(hr != S_OK) { hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, gcap.pVCap, NULL, NULL); if(hr == VFW_S_NOPREVIEWPIN) { // preview was faked up for us using the (only) capture pin gcap.fPreviewFaked = TRUE; } else if(hr != S_OK) { // ErrMsg(TEXT("Cannot render video preview stream")); goto SetupCaptureFail; } } } // // Render the closed captioning pin? It could be a CC or a VBI category pin, // depending on the capture driver // if(gcap.fCapCC) { hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_CC, NULL, gcap.pVCap, NULL, gcap.pRender); if(hr != NOERROR) { hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL, gcap.pVCap, NULL, gcap.pRender); if(hr != NOERROR) { // ErrMsg(TEXT("Cannot render closed captioning")); // so what? goto SetupCaptureFail; } } // To preview and capture VBI at the same time, we can call this twice if(gcap.fWantPreview) { hr = gcap.pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL, gcap.pVCap, NULL, NULL); } } // // Get the preview window to be a child of our app's window // // This will find the IVideoWindow interface on the renderer. It is // important to ask the filtergraph for this interface... do NOT use // ICaptureGraphBuilder2::FindInterface, because the filtergraph needs to // know we own the window so it can give us display changed messages, etc. if(gcap.fWantPreview) { hr = gcap.pFg->QueryInterface(IID_IVideoWindow, (void **)&gcap.pVW); if(hr != NOERROR && gcap.fWantPreview) { // ErrMsg(TEXT("This graph cannot preview")); } else if(hr == NOERROR) { gcap.pVW->put_Owner((OAHWND)m_hWnd); // We own the window now gcap.pVW->put_WindowStyle(WS_CHILD); // you are now a child // give the preview window all our space but where the status bar is gcap.pVW->SetWindowPosition(rectVideo.left, rectVideo.top, rectVideo.Width(), rectVideo.Height()); // be this big gcap.pVW->put_Visible(OATRUE); } } // now tell it what frame rate to capture at. Just find the format it // is capturing with, and leave everything alone but change the frame rate hr = gcap.fUseFrameRate ? E_FAIL : NOERROR; if(gcap.pVSC && gcap.fUseFrameRate) { hr = gcap.pVSC->GetFormat(&pmt); if(hr == NOERROR) {// DV capture does not use a VIDEOINFOHEADER if(pmt->formattype == FORMAT_VideoInfo) { VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *)pmt->pbFormat; pvi->AvgTimePerFrame = (LONGLONG)(10000000 / gcap.FrameRate); hr = gcap.pVSC->SetFormat(pmt); } DeleteMediaType(pmt); } } // if(hr != NOERROR) ErrMsg(TEXT("Cannot set frame rate for capture")); // now ask the filtergraph to tell us when something is completed or aborted // (EC_COMPLETE, EC_USERABORT, EC_ERRORABORT). This is how we will find out // if the disk gets full while capturing // hr = gcap.pFg->QueryInterface(IID_IMediaEventEx, (void **)&gcap.pME); // if(hr == NOERROR) gcap.pME->SetNotifyWindow((OAHWND)m_hWnd, WM_FGNOTIFY, 0); // potential debug output - what the graph looks like // DumpGraph(gcap.pFg, 1); // Add our graph to the running object table, which will allow // the GraphEdit application to "spy" on our graph #ifdef _DEBUG hr = AddGraphToRot(gcap.pFg, &gdwGraphRegister); if(FAILED(hr)) { // ErrMsg(TEXT("Failed to register filter graph with ROT! hr=0x%x"), hr); gdwGraphRegister = 0; } #endif // All done. gcap.fCaptureGraphBuilt = TRUE; return TRUE; SetupCaptureFail: TearDownGraph(); return FALSE; }
Finalement voici ma méthode de setCaptureFile:
Un bonbeck a celui qui comprend pourquoi mon fichier qui est donc tout géré par directX9 n est pas lisible après coup. J'ai eu beau regarder partout si un tag de disponibilité de fichier existe... c est peine perdue... je ne trouve pas...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 BOOL SetCaptureFile2(HWND hWnd,CString imagFichier) { USES_CONVERSION; char path[100]; sprintf(path,"%s",imagFichier); LPTSTR pszName = gcap.szCaptureFile;; LPTSTR p; TCHAR szFileName[_MAX_PATH]; TCHAR szBuffer[_MAX_PATH] ; // start with capture file as current file name szFileName[0] = 0; lstrcpyn(szFileName, gcap.szCaptureFile, NUMELMS(szFileName)); // Get just the path info // Terminate the full path at the last backslash lstrcpyn(szBuffer, szFileName, NUMELMS(szBuffer)); for(p = szBuffer + lstrlen(szBuffer); p > szBuffer; p--) { if(*p == '\\') { *(p+1) = '\0'; break; } } szBuffer[_MAX_PATH-1] = 0; // Null-terminate // We have a capture file name lstrcpyn(szFileName,path, _MAX_PATH); lstrcpyn(pszName, szFileName, _MAX_PATH); lstrcpyn(gcap.szCaptureFile , pszName,_MAX_PATH); // If this is a new file, then invite the user to // allocate some space #ifdef UNICODE // Convert Multibyte string to ANSI char szCaptureFile[STR_MAX_LENGTH]; int rc = WideCharToMultiByte(CP_ACP, 0, gcap.szCaptureFile, -1, szCaptureFile, STR_MAX_LENGTH, NULL, NULL); #else TCHAR *szCaptureFile = gcap.szCaptureFile; #endif // bring up dialog, and set new file size BOOL f = AllocCaptureFile(hWnd); if(!f) return FALSE; // tell the file writer to use the new filename if(gcap.pSink) gcap.pSink->SetFileName(T2W(gcap.szCaptureFile), NULL); return TRUE; }
Voici aussi donc mes deux fonctions de début et fin de capture. La fin de capture est appelée par un evenement timer (je suppose que je connais toujours le nombre de millisecondes qu'il faut capturer)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 BOOL fHasStreamControl; HRESULT hr; if(gcap.fCapturing) return TRUE; // or we'll get confused if(gcap.fPreviewing) StopPreview(); // or we'll crash if(!gcap.fCaptureGraphBuilt) return FALSE; // This amount will be subtracted from the number of dropped and not // dropped frames reported by the filter. Since we might be having the // filter running while the pin is turned off, we don't want any of the // frame statistics from the time the pin is off interfering with the // statistics we gather while the pin is on gcap.lDroppedBase = 0; gcap.lNotBase = 0; REFERENCE_TIME start = 0, // MAX_TIME, stop = gcap.dwTimeLimit; // MAX_TIME; // don't capture quite yet... hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, NULL, &start, NULL, 0, 0); // Do we have the ability to control capture and preview separately? fHasStreamControl = SUCCEEDED(hr); //- prepare to run the graph IMediaControl *pMC = NULL; hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC); if(FAILED(hr)) { // ErrMsg(TEXT("Error %x: Cannot get IMediaControl"), hr); return FALSE; } // If we were able to keep capture off, then we can // run the graph now for frame accurate start later yet still showing a // preview. Otherwise, we can't run the graph yet without capture // starting too, so we'll pause it so the latency between when they // press a key and when capture begins is still small (but they won't have // a preview while they wait to press a key) if(fHasStreamControl) hr = pMC->Run(); else hr = pMC->Pause(); if(FAILED(hr)) { // stop parts that started pMC->Stop(); pMC->Release(); // ErrMsg(TEXT("Error %x: Cannot start graph"), hr); return FALSE; } /*// press a key to start capture f = DoDialog(ghwndApp, IDD_PressAKeyDialog, (DLGPROC)PressAKeyProc, 0); if(!f) { pMC->Stop(); pMC->Release(); if(gcap.fWantPreview) { BuildPreviewGraph(); StartPreview(); } return f; }*/ // Start capture NOW! if(fHasStreamControl) { if(!gcap.pDF){ // IAMDroppedFrames* we may not have this yet hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, gcap.pVCap, IID_IAMDroppedFrames, (void **)&gcap.pDF); if(hr != NOERROR) hr = gcap.pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, gcap.pVCap, IID_IAMDroppedFrames, (void **)&gcap.pDF); } // turn the capture pin on now! hr = gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE, NULL, NULL, NULL, &stop, 0, 0); // make note of the current dropped frame counts if(gcap.pDF){ gcap.pDF->GetNumDropped(&gcap.lDroppedBase); gcap.pDF->GetNumNotDropped(&gcap.lNotBase); } } // if(fHasStreamControl else { hr = pMC->Run(); if(FAILED(hr)) { //-stop parts that started pMC->Stop(); pMC->Release(); // ErrMsg(TEXT("Error %x: Cannot run graph"), hr); return FALSE; } } pMC->Release(); gcap.lCapStartTime = timeGetTime();// when did we start capture? // update status bar 30 times per second - #captured, #dropped // SetTimer(ghwndApp, 1, 33, NULL); gcap.fCapturing = TRUE; return TRUE; }Heu franchement, celui qui m'aide aura fait sa BA pour des mois, car je sais que c est long... pas forcément dur, mais long.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 // stop the capture graph // BOOL StopCapture() { if(!gcap.fCapturing) return FALSE; //- stop the graph IMediaControl *pMC = NULL; HRESULT hr = gcap.pFg->QueryInterface(IID_IMediaControl, (void **)&pMC); if(SUCCEEDED(hr)) { hr = pMC->Stop(); pMC->Release(); } if(FAILED(hr)) { // ErrMsg(TEXT("Error %x: Cannot stop graph"), hr); return FALSE; } gcap.lCapStopTime = timeGetTime();// when the graph was stopped // no more status bar updates // KillTimer(ghwndApp, 1); // one last time for the final count and all the stats UpdateStatus(TRUE); gcap.fCapturing = FALSE; // get rid of menu garbage // InvalidateRect(ghwndApp, NULL, TRUE); return TRUE; }
Désolé et merci aux courageux![]()
Partager