Video Tool Box. pixel transfer, when to release source buffer? - video-toolbox

One simple straight question, when to release source pixelbuffer after transferred image to avoid crash:
//pixel_buffer is the original
CVPixelBufferCreate(kCFAllocatorDefault,
CVPixelBufferGetWidth(pixel_buffer),
CVPixelBufferGetHeight(pixel_buffer),
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
NULL, &targetPxb);
if (targetPxb != NULL) {
auto status = VTPixelTransferSessionTransferImage(transSession,
pixel_buffer,
targetPxb);
if (status == noErr) {
// CFRelease(pixel_buffer); //this will cause crash
}
}

Check the reference count of pixel_buffer. If you did not add a CFRetain, executing a CFRelease will cause your application to crash since the reference count is already 0, so there is no need to call CFRelease in this case. There are simple ways of checking the reference count, for example:
CFGetRetainCount

Related

CreateIconFromResource return NULL and UI crashed after being called thousands of times

Anybody got this problem, anyway I didn't find an answer. The code is simple:
void CbDlg::OnBnClickedOk()
{
for(int i=0; i<1000; i++)
{
HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(IDR_MAINFRAME), RT_GROUP_ICON);
HGLOBAL hResLoad = ::LoadResource(NULL, hRes);
BYTE* pIconBytes = (BYTE*)::LockResource(hResLoad);
int nId = ::LookupIconIdFromDirectory(pIconBytes, TRUE);
hRes = ::FindResource(NULL, MAKEINTRESOURCE(nId), RT_ICON);
DWORD read = ::SizeofResource(NULL ,hRes);
hResLoad = ::LoadResource(NULL, hRes);
pIconBytes = (BYTE*)::LockResource(hResLoad);
if(pIconBytes != NULL)
{
HICON hIcon = ::CreateIconFromResource(pIconBytes, read, TRUE, 0x00030000);
DWORD e = ::GetLastError();
if(hIcon != NULL)
{
::DestroyIcon(hIcon);
}
}
}
}
If I click the Ok button four times (On my computer), CreateIconFromResource start to return NULL (It worked fine before and I could even draw out the icon). As to the GetLastError, it's always return 6 whatever CreateIconFromResource return NULL or not.
When this problem happened, if I drag the title bar to move, UI crashed, see the pictrue.
Of course you can understand this piece of code is just a demo, my real business need to call CreateIconFromResource thousands of times just like this.
UPDATE:
According to Hans' suggestion, I keep tracking the Handles/USER Objects/GDI objects, and found that USER Objects grows 1000 and GDI objects grows 2000 against each clicking to OK button (handles didn't grow), and GDI objects is 9999 when problem happens. But how to release them correctly, when I finish to use? I didn't use that much at one time, but need to load, release, load again, release again... Just like this demo. As MSDN document, I called DestroyIcon for every HICON. What else do I need to do, to finally release the USER/GDI objects?
I found the answer. The success or failure is all due to MSDN.
It says:
"The CreateIconFromResource function calls CreateIconFromResourceEx passing LR_DEFAULTSIZE|LR_SHARED as flags" AND "Do not use this function(DestroyIcon) to destroy a shared icon"
But It also says:
"When you are finished using the icon, destroy it using the DestroyIcon function" in CreateIconFromResource's document.
Actually, the second statement is WRONG.
So, the solution is, using CreateIconFromResourceEx without LR_SHARED, and DestroyIcon every HICON after using.

OSX 10.10 file change event from dispatch queue

I try to monitor file changes on OSX 10.10, starting with a fresh Cocoa application in Xcode, just adding the following code.
If I uncomment the last line in the snippet then I receive the file change events perfectly fine. But I can not make this last call because it should be a Cocoa GUI application.
I digged through a lot of documentation and can't find my error. Do I have to initialize or start this whole dispatch subsystem somehow?
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
int fd = open("<FILENAME>", O_EVTONLY);
if (fd == -1) return;
dispatch_queue_t qu = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
if (!qu) {
printf("can not get queue");
return;
}
unsigned long mask =
DISPATCH_VNODE_DELETE |
DISPATCH_VNODE_WRITE |
DISPATCH_VNODE_EXTEND |
DISPATCH_VNODE_ATTRIB |
DISPATCH_VNODE_LINK |
DISPATCH_VNODE_RENAME |
DISPATCH_VNODE_REVOKE;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, mask, qu);
printf("source created\n");
if (!source) {
close(fd);
return;
}
printf("source valid\n");
dispatch_source_set_event_handler(source, ^{
printf("FILE CHANGED\n");
});
dispatch_resume(source);
printf("source resumed\n");
// If I call dispatch_main() I will receive the file system events as expected.
// But as a Cocoa application, I must not call this.
// Instead, I was under the impression that NSApplicationMain handles this.
//dispatch_main();
}
Grand Central Dispatch objects, such as dispatch sources, are automatically retained and released by ARC in recent versions of the compiler and frameworks.
At the end of your method, the last strong reference to source is lost and ARC is issuing an automatic dispatch_release(source). (It would also release the queue, but the source has another strong reference to that. So, if the source survived, so would the queue.)
You need to keep a strong reference to the source in an instance variable.

Why does CMSampleBufferGetImageBuffer return NULL

I have built some code to process video files on OSX, frame by frame. The following is an extract from the code which builds OK, opens the file, locates the video track (only track) and starts reading CMSampleBuffers without problem. However each CMSampleBufferRef I obtain returns NULL when I try to extract the pixel buffer frame. There's no indication in iOS documentation as to why I could expect a NULL return value or how I could expect to fix the issue. It happens with all the videos on which I've tested it, regardless of capture source or CODEC.
Any help greatly appreciated.
NSString *assetInPath = #"/Users/Dave/Movies/movie.mp4";
NSURL *assetInUrl = [NSURL fileURLWithPath:assetInPath];
AVAsset *assetIn = [AVAsset assetWithURL:assetInUrl];
NSError *error;
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:assetIn error:&error];
AVAssetTrack *track = [assetIn.tracks objectAtIndex:0];
AVAssetReaderOutput *assetReaderOutput = [[AVAssetReaderTrackOutput alloc]
initWithTrack:track
outputSettings:nil];
[assetReader addOutput:assetReaderOutput];
// Start reading
[assetReader startReading];
CMSampleBufferRef sampleBuffer;
do {
sampleBuffer = [assetReaderOutput copyNextSampleBuffer];
/**
** At this point, sampleBuffer is non-null, has all appropriate attributes to indicate that
** it's a video frame, 320x240 or whatever and looks perfectly fine. But the next
** line always returns NULL without logging any obvious error message
**/
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
if( pixelBuffer != NULL ) {
size_t width = CVPixelBufferGetWidth(pixelBuffer);
size_t height = CVPixelBufferGetHeight(pixelBuffer);
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
...
other processing removed here for clarity
}
} while( ... );
To be clear, I've stripped all error checking code but no problems were being indicated in that code. i.e. The AVAssetReader is reading, CMSampleBufferRef looks fine etc.
You haven't specified any outputSettings when creating your AVAssetReaderTrackOutput. I've run into your issue when specifying "nil" in order to receive the video track's original pixel format when calling copyNextSampleBuffer. In my app I wanted to ensure no conversion was happening when calling copyNextSampleBuffer for the sake of performance, if this isn't a big concern for you, specify a pixel format in the output settings.
The following are Apple's recommend pixel formats based on the hardware capabilities:
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
Because you haven't supplied any outputSettings you're forced to use the raw data contained within in the frame.
You have to get the block buffer from the sample buffer using CMSampleBufferGetDataBuffer(sampleBuffer), after you have that you need to get the actual location of the block buffer using
size_t blockBufferLength;
char *blockBufferPointer;
CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &blockBufferLength, &blockBufferPointer);
Look at *blockBufferPointer and decode the bytes using the frame header information for your required codec.
FWIW: Here is what official docs say for the return value of CMSampleBufferGetImageBuffer:
"Result is a CVImageBuffer of media data. The result will be NULL if the CMSampleBuffer does not contain a CVImageBuffer, or if the CMSampleBuffer contains a CMBlockBuffer, or if there is some other error."
Also note that the caller does not own the returned dataBuffer from CMSampleBufferGetImageBuffer, and must retain it explicitly if the caller needs to maintain a reference to it.
Hopefully this info helps.

PDFDocument dataRepresentation is slow... How to show progress?

I'm currently working on a Cocoa application that works with PDFs, and am using Apple's PDFKit to do the work. Saving the PDF is proving a problem, as I'd like to show a progress bar while that's happening, which doesn't seem to be possible using the standard writeToURL: method. So, I went and used Grand Central Dispatch instead.
The problem with this is that the dataRepresentation method used to get the NSData to write is terribly slow in converting any PDF larger than ~3MB to NSData, and so the progress bar stalls for a few seconds while my program is waiting for the data, which seems to make the users think the program has stalled completely. And I don't really want them thinking that.
My question is, is there anything I can do to either speed up the dataRepresentation method, or report its progress to the user?
Here's the Grand Central Dispatch code I ended up writing:
NSData *pdf = [doc dataRepresentation]; // R-e-a-l-l-y S-l-o-w
dispatch_queue_t queue = dispatch_get_current_queue();
dispatch_data_t dat = dispatch_data_create([pdf bytes], [pdf length], queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
__block dispatch_io_t channel =
dispatch_io_create_with_path(DISPATCH_IO_STREAM,
[path UTF8String], // Convert to C-string
O_WRONLY, // Open for writing
0, // No extra flags
queue,
^(int error){
// Cleanup code for normal channel operation.
// Assumes that dispatch_io_close was called elsewhere.
if (error == 0) {
dispatch_release(channel);
channel = NULL;
}
});
// The next two lines make sure that the block in dispatch_io_write
// gets called about 60 times, so that it can update the progress bar.
dispatch_io_set_low_water(channel,[pdf length]/60);
dispatch_io_set_high_water(channel,[pdf length]/60);
dispatch_io_write(channel,
0,
dat,
queue,
^(bool done, dispatch_data_t data, int error) {
if (data) {
// Increment progress bar
}
if (error) {
// Handle errors
}
if (done) {
dispatch_io_close(channel, NULL);
}
});
dispatch_release(dat);
dispatch_release(queue);
Use the PDFDocumentDidBeginPageWriteNotification and PDFDocumentDidEndPageWriteNotification notifications, which are sent during -writeToURL:. The notifications tell you what page is processing, which you can compare to the total number of pages in the document to show progress.

MFC Printing Issue - Access Violation Exception

I'm having an issue with some MFC printing code lately. To describe the issue at a high level, we're getting problems when we try to print the same document twice. (Really, it doesn't have to be the SAME document. It seems that it happens when two documents are printed and the child window isn't destroyed and created between them.) The first time, the document prints fine. The second time, the program crashes with an uncaught exception.
This also only started happening after a recent upgrade. This is code that I've literally never touched. It worked fine in version 5 when we were using VS2005 and MFC8. But then, when we upgraded to VS2008 and MFC9, this started happening.
Here's the problem code:
void CReportWnd::OnPrint()
{
CDC dc;
CPrintDialog dlg (FALSE);
CPrintDialog defaults(FALSE);
DEVMODE *ldev_printinfo;
int li_first = 0;
int li_last;
int ret = defaults.GetDefaults();
ldev_printinfo = defaults.GetDevMode();
//ldev_printinfo->dmOrientation = DMORIENT_LANDSCAPE;
dc.Attach (defaults.GetPrinterDC ());
dc.ResetDC(ldev_printinfo);
PROPrint(1, NULL, &dc, NULL, &li_last, true);
dlg.m_pd.hDevMode = ldev_printinfo;
dlg.m_pd.Flags &= ~PD_NOPAGENUMS;
dlg.m_pd.nMinPage = 1;
dlg.m_pd.nFromPage = 1;
dlg.m_pd.nMaxPage = li_last;
dlg.m_pd.nToPage = li_last;
if (dlg.DoModal () == IDOK) {
dc.DeleteDC();
dc.Detach();
dc.Attach (dlg.GetPrinterDC ());
} else {
return;
}
//Set up document info (need to set the name)
DOCINFO di;
::ZeroMemory (&di, sizeof (DOCINFO));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = "Report";
if(dc.StartDoc(&di) <= 0) {
return;
}
if(dlg.PrintRange()) {
li_first = dlg.m_pd.nFromPage - 1;
li_last = dlg.m_pd.nToPage - 1;
}
//Now do the actual print job to the printing device
PROPrint(1, NULL, &dc, &li_first, &li_last, false);
}
On the int ret = ... line near the top is where the exception is thrown. GetDefaults() throws an access violation exception. But again, only the second time that this function is called. It seems to me like it's some sort of resource issue. Like a resource isn't being freed and allocated properly. But I'm so inexperienced with printing that it could be anything.
If anybody could offer any kind of help, I would really appreciate it.
Also, yes, I know that I can just catch the exception. My issue is, how do I handle the exception and still print the document?
EDIT:
It seems as if our program is having this issue in multiple places, not just with this particular set of code. It makes me think that the issue might not be in this code specifically. I'm working on it now and I kind of have to get it fixed, so when I figure out the solution, I'll be sure to post back. Until then, I'm always open to suggestions.
Not sure, but could it be because the code is deleting the default device context associated with the default printer? Try removing the dc.DeleteDC() line to give you:
if (dlg.DoModal() == IDOK)
{
dc.Detach();
dc.Attach(dlg.GetPrinterDC());
}
else
return;

Resources