|
I cover this in a demo in my MFC book (see sig). It's a fairly involved process, but here are the basic steps. Let me know if you need any additional help and I'll send you a demo app.
- Create a member variable to store the current tab index.
- Create an array member variable to hold all of the index values for the currently disabled tabs.
- Handle the TCN_SELCHANGING notification message to set the current tab index.
- Handle the TCN_SELCHANGED notification message to decide if you want to enable the tab to be activated. If you don’t, you must post a PSM_SETCURSEL message. In this message, the last active tab index is specified.
Let me know if you need it and I'll send you the demo app from the book
Cheers,
Tom Archer
Author - Inside C#, Visual C++.NET Bible
|
|
|
|
|
thanks for the help
i see that there is no standart way of doing this...
this is realy a good way
thanks
(will try it this weekend)
|
|
|
|
|
|
In an avi you've got keyframes and other frames. for example, you can have a keyframe each 100 frames.
I'm using the function AVIStreamWrite(...) from the avifile library.
You can pass AVIIF_KEYFRAME for the DWFLAGS parameter when the frame you're going to write is a keyframe.
ok, so far so good.
But, what if you want to write a non-keyframe?
Can we get that dwflag from the source stream? (to write raw data without processing)
I'm running around in circles for ages now
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
Here's a snippet I have used for writing AVI files. I have always used DWFLAGS = 0 for normal frames.
// Get a frame
CCapture::SnatchDIB( &m_rcWindow,
m_hwndWindow,
m_hdcWindow,
&pDIB,
&m_rcBitmap,
m_bWindowNotAligned);
LPBITMAPINFO pBitmapInfo = (LPBITMAPINFO)pDIB;
// Is it time for a key frame?
if (dwNextKeyFrame <= 0) {
dwFrameType = AVIIF_KEYFRAME;
dwNextKeyFrame = m_CaptureProfile.dwVideoFPS;
} else {
dwFrameType = 0;
}
// Write it to the stream
if (pBitmapInfo != NULL) {
TRACE("Writing frame %d...\n", dwFramesWritten);
AVIStreamWrite( m_pAVIStreamCompressed,
dwFramesWritten,
1,
(LPBYTE)pBitmapInfo + pBitmapInfo->bmiHeader.biSize + pBitmapInfo->bmiHeader.biClrUsed * sizeof(RGBQUAD),
pBitmapInfo->bmiHeader.biSizeImage,
dwFrameType,
NULL,
NULL);
dwFramesWritten++;
dwNextKeyFrame--;
}
--
Dana Holt
Xenos Software
|
|
|
|
|
tnx Dana, but this is only part of the solution.
If you've got a source avi, how can you know if a frame is a keyframe or a normal frame? Is there a function that gets something like a KEYframe-rate, cause not all streams use 1/100?
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
After reading your message again I see the other part you are asking.
I've never read from one AVI stream into another. I have only created new AVI files from bitmaps. In this case I just let the user decide how often to insert key frames.
After a quick search of MSDN this looks like a good lead:
The AVICOMPRESSOPTIONS structure contains information about a stream and how it is compressed and saved.
I have used this structure to create compressed streams, but you may also be able to get the structure for a source stream. I wish I could be of more help.
Regards,
--
Dana Holt
Xenos Software
|
|
|
|
|
Is there a place to get/view some c++ code, concerning the avifile-api?
msdn is not helping here, cause it misses some pages about the topic (avistreamwrite is missing...).
It appears that the codec for div-x avifiles cannot be used, returns no codec for ICOPEN on a div-x avistream. correct?
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
frisco wrote:
Is there a place to get/view some c++ code, concerning the avifile-api?
msdn is not helping here, cause it misses some pages about the topic (avistreamwrite is missing...).
When I wrote my last AVI code I used MSDN, but it is a little lacking, so I had to experiment a lot to get it working.
frisco wrote:
It appears that the codec for div-x avifiles cannot be used, returns no codec for ICOPEN on a div-x avistream. correct?
I have used both the DIVX v3 and v4 codecs to encode AVI files without any problems. I am planning to submit an article about video encoding here as soon as I get a chance.
Here's some code that I used to enumerate codecs, display codec info, and open the codec's config dialog. It works with all codecs I have tested it with, including DIVX v3 and v4.
BOOL CPPVideoDestOptionsCompression::PopulateCompressors()
{
BITMAPINFO bitmap;
CAPTUREPROFILE captureProfile;
// Show status dialog
CDlgStatus *pDlg = new CDlgStatus;
pDlg->Create(IDD_STATUS);
pDlg->SetStatusText("Looking for video compressors...");
pDlg->Invalidate();
pDlg->ShowWindow(SW_SHOW);
CCapture::GetCurrentProfile(&captureProfile);
// Zero bitmap info header
ZeroMemory((PVOID)&bitmap, sizeof(BITMAPINFO));
// Load screen info
HDC hdcScreen;
hdcScreen = ::GetDC(NULL);
bitmap.bmiHeader.biWidth = 20;
bitmap.bmiHeader.biHeight = 20;
bitmap.bmiHeader.biBitCount = ::GetDeviceCaps(hdcScreen, BITSPIXEL);
bitmap.bmiHeader.biPlanes = ::GetDeviceCaps(hdcScreen, PLANES);
::ReleaseDC(NULL, hdcScreen);
int compressorNumber = 0;
int nSelectedCompressor = 0;
ICINFO compressorInfo;
for(int i = 0; i < 30;i++) {
ICInfo(ICTYPE_VIDEO, i, &compressorInfo);
HIC hic;
hic = ICOpen( compressorInfo.fccType,
compressorInfo.fccHandler,
ICMODE_QUERY);
if (hic) {
if (ICCompressQuery(hic, &bitmap, NULL) == ICERR_OK) {
if (ICGetInfo(hic, &compressorInfo, sizeof(ICINFO))) {
// Add to combo box
char szName[16];
WideCharToMultiByte( CP_ACP,
0,
compressorInfo.szName,
-1,
szName,
16,
NULL,
NULL);
int pos = m_cboxVideoCompressor.AddString(szName);
TRACE("CPPVideoDestOptionsCompression::PopulateCompressors() - Adding %s...\n", szName);
// Add it to the list
m_CompressorInfo[compressorNumber] = compressorInfo;
// Is this the one in the profile?
if (compressorInfo.fccHandler == captureProfile.dwVideoFCCHandler)
nSelectedCompressor = pos;
// Next compressor, please!
compressorNumber++;
}
}
m_cboxVideoCompressor.SetCurSel(nSelectedCompressor);
ICClose(hic);
}
}
if (compressorNumber == 0) {
MessageBox("There are no video compressors installed that are compatible with your current display settings.",
APP_NAME, MB_ICONINFORMATION|MB_TOPMOST);
m_cboxVideoCompressor.EnableWindow(FALSE);
GetDlgItem(IDC_BTN_VIDEO_DEST_OPT_COMPRESSION_COMP_INFO)->EnableWindow(FALSE);
GetDlgItem(IDC_BTN_VIDEO_DEST_OPT_COMPRESSION_COMP_SETTINGS)->EnableWindow(FALSE);
GetDlgItem(IDC_STC_VIDEO_DEST_OPT_COMPRESSION_COMP_NUMBER)->SetWindowText("No compressors found.");
m_sliderVideoQuality.EnableWindow(FALSE);
m_sliderDataRate.EnableWindow(FALSE);
} else {
CString foo;
foo.Format("Found %d video %s.", compressorNumber,
(compressorNumber) > 1 ? "codecs" : "codec");
GetDlgItem(IDC_STC_VIDEO_DEST_OPT_COMPRESSION_COMP_NUMBER)->SetWindowText(foo);
}
pDlg->DestroyWindow();
delete pDlg;
return TRUE;
}
void CPPVideoDestOptionsCompression::OnSelchangeVideoCompressor()
{
// TODO: Add your control notification handler code here
int nSelectedCompressor;
nSelectedCompressor = m_cboxVideoCompressor.GetCurSel();
if (nSelectedCompressor != CB_ERR) {
// Open the current compressor
HIC hic;
hic = ICOpen( mmioFOURCC('V','I','D','C'),
m_CompressorInfo[nSelectedCompressor].fccHandler,
ICMODE_QUERY);
// Did it open ok?
if (hic == 0) {
CString foo;
char szName[16];
WideCharToMultiByte( CP_ACP,
0,
m_CompressorInfo[nSelectedCompressor].szName,
-1,
szName,
16,
NULL,
NULL);
foo.Format("Could not query compressor for existence of configuration dialog:\r\n%s", szName);
MessageBox(foo, APP_NAME, MB_ICONSTOP|MB_TOPMOST);
}
// Does this compressor have a configuration dialog?
if (ICQueryConfigure(hic) != ICERR_OK) { // Weird
GetDlgItem(IDC_BTN_VIDEO_DEST_OPT_COMPRESSION_COMP_CONFIG)->EnableWindow(TRUE);
GetDlgItem(IDC_BTN_VIDEO_DEST_OPT_COMPRESSION_COMP_RESET)->EnableWindow(TRUE);
} else {
GetDlgItem(IDC_BTN_VIDEO_DEST_OPT_COMPRESSION_COMP_CONFIG)->EnableWindow(FALSE);
GetDlgItem(IDC_BTN_VIDEO_DEST_OPT_COMPRESSION_COMP_RESET)->EnableWindow(FALSE);
}
// Close compressor
ICClose(hic);
// Update configuration
CPSVideoDestOptions::m_CaptureProfile.dwVideoFCCHandler =
m_CompressorInfo[nSelectedCompressor].fccHandler;
}
}
void CPPVideoDestOptionsCompression::OnBtnVideoCompInfo()
{
// Open the selected compressor
HIC hic;
int nSelection;
nSelection = m_cboxVideoCompressor.GetCurSel();
hic = ICOpen( m_CompressorInfo[nSelection].fccType,
m_CompressorInfo[nSelection].fccHandler,
ICMODE_QUERY);
// Did we open it?
if (hic != 0) {
// Does this compressor have an "about" box?
if (ICAbout(hic, -1) == ICERR_OK) {
ICAbout(hic, GetSafeHwnd());
} else {
MessageBox( "No compressor information is available.",
APP_NAME,
MB_ICONINFORMATION|MB_TOPMOST);
}
// Close the compressor
ICClose(hic);
}
}
void CPPVideoDestOptionsCompression::OnCompConfig()
{
// Get the currently selected compressor
int nSelectedCompressor;
nSelectedCompressor = m_cboxVideoCompressor.GetCurSel();
// Is there a compressor selected
if (nSelectedCompressor == CB_ERR) {
return;
}
// Open the current compressor
HIC hic;
hic = ICOpen( mmioFOURCC('V','I','D','C'),
m_CompressorInfo[nSelectedCompressor].fccHandler,
ICMODE_QUERY);
// Did it open ok?
if (hic == 0) {
CString foo;
char szName[16];
WideCharToMultiByte( CP_ACP,
0,
m_CompressorInfo[nSelectedCompressor].szName,
-1,
szName,
16,
NULL,
NULL);
foo.Format("Could not open compressor:\r\n%s", szName);
MessageBox(foo, APP_NAME, MB_ICONSTOP|MB_TOPMOST);
return;
}
// Do we already have a save state for this compressor?
DWORD dwStateBytes;
BYTE *pState = NULL;
if (::CompressorLoadState(&m_CompressorInfo[nSelectedCompressor], NULL, &dwStateBytes)) {
pState = new BYTE[dwStateBytes];
// Yes, so load our saved state
if (::CompressorLoadState(&m_CompressorInfo[nSelectedCompressor], (LPVOID*)&pState, &dwStateBytes)) {
// Loaded it, now set it
ICSetState(hic, (LPVOID)pState, dwStateBytes);
}
delete[] pState;
}
// Display codec configuration dialog
ICConfigure( hic,
GetSafeHwnd());
// Get amount of space for compressor options
DWORD nStateBytes;
nStateBytes = ICGetStateSize(hic);
// Allocated the space
pState = new BYTE[nStateBytes];
// Get the current state
ICGetState( hic,
(PVOID)pState,
nStateBytes);
// Close the compressor
ICClose(hic);
// Save state to file
if (!CompressorSaveState(&m_CompressorInfo[nSelectedCompressor], pState, nStateBytes)) {
MessageBox("Error saving compressor state.", APP_NAME, MB_ICONSTOP|MB_TOPMOST);
}
// Free state memory
delete[] pState;
}
Wow, that's a long message!
--
Dana Holt
Xenos Software
|
|
|
|
|
A long message indead, and a helping one too!
Experimenting because you don't find any doc on the net, that's what I'm doing here all the time
I'm certainly gonna check you code, but could that div-x problem have something to do with that new div-x/playa thing?
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
frisco wrote:
I'm certainly gonna check you code, but could that div-x problem have something to do with that new div-x/playa thing?
I don't think the divx playa would cause you any codec problems but anything is possible.
I also saw that DIVX5 is out now from www.divx.com.
--
Dana Holt
Xenos Software
|
|
|
|
|
An article on that topic would be nice
In fact, I'm a java-programmer (but I've learned turbo c++ at school some years ago). And I was able to write a program in java that did the basic video/audio editing in less than half a day. That's because the sources for java are much more available, starting with the site from sun microsystems that's way better than the msdn antipode.
I think this is the problem for c++: you can't find anything, have to experiment all the time.
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
frisco wrote:
An article on that topic would be nice
I have the code working. I need to put it into a class, tidy it up, and add support for an audio stream to be encoded into the AVI.
frisco wrote:
I think this is the problem for c++: you can't find anything, have to experiment all the time.
C/C++ lets you do so much that you can get into trouble quickly. For example, writing/reading through bad pointers. I started with C and assembly language under DOS, so I have learned to avoid these pitfalls most of the time.
Some parts of the Windows API docs are very lacking or even misleading. This is what causes me most of my frustration these days.
--
Dana Holt
Xenos Software
|
|
|
|
|
I've found something else.
The AVISTREAMINFO returns DIV4 for fcctype but the BITMAPINFOHEADER returns DIV3 for compressor. I guess that's normal?
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
frisco wrote:
I've found something else.
The AVISTREAMINFO returns DIV4 for fcctype but the BITMAPINFOHEADER returns DIV3 for compressor. I guess that's normal?
That is weird. I have never done any decoding, so I have no idea what kind of horrors to expect.
What function are you using to get this information loaded into BITMAPINFOHEADER?
--
Dana Holt
Xenos Software
|
|
|
|
|
BITMAPINFOHEADER bi;
AVIStreamReadFormat(pStream,0,&bi,&lStreamSize);
[VISUAL STUDIO 6.0] [MFC] [WIN98/2]
Bluute tette!
|
|
|
|
|
NE1 know where I can find a class/implemenation of a keyboard that can be popped up for entry into an app?
Thanks
|
|
|
|
|
I'm writing an addin and i want it to obtain the path of the workspace file on "closing the workspace" event.
How should i get it?
rechi
|
|
|
|
|
What should i do to make this work:
bool running=true;
void CXDlg::OnCancelButton() {
running=false;
}
void CXDlg::OnStartButton() {
while (running) {
//if i'm running this loop i cannot
//click on the cancel button (in fact
//i can but it doesn't work )
}
}
If you know VB you know that there is a function called DoEvents(). But i can't find somethig like that in C. Please help if you can.
Thanks
Vis
|
|
|
|
|
You should place the loop in a separate worker thread, otherwise you'll normaly block the message loop and the cancel message will never be treated.
rechi
|
|
|
|
|
And how can i create a new thread?
Vis
|
|
|
|
|
Use AfxBeginThread. Pass a thread function and create a meessage loop inside of it. Then use PostThreadMessage to communicate with the thread.
rechi
|
|
|
|
|
You should give the UI chance to process the mesgs.
Put the foll. function in your app and call it
in the loop.
void CMyApp::ProcesssQueuedMsgs()
{
MSG msg ;
// User tries to close the application during a lengthy process causes a crash,
// (The window object are destroyed when this mesg is processed) so exclude WM_CLOSE.
while( PeekMessage(&msg, NULL,0,0,PM_NOREMOVE) && msg.message != WM_CLOSE )
{
AfxGetApp ()->PumpMessage () ;
}
LONG lIdle = 0;
while (AfxGetApp ()->OnIdle (lIdle++));
}
In OnCancel()
set the 'running' to false and the code will work.
|
|
|
|
|
This should be the best way rather than spwning a new thread.
|
|
|
|
|
Thx for the comment, that is more than what I can say for the
fellows who r actually using this code and dont bother with
a reply.
|
|
|
|
|