objective-cmacosvideoavfoundationinterlacing

read video metadata in objective c


I am trying to see whether a video file the user has selected is interlaced/progressive, and then do some manipulation depending on that.

I have tried to check whether the cmsamplebuffer i have extracted is defined as top field first or bottom field first however this returns null for all inputs.

NSMutableDictionary *pixBuffAttributes = [[NSMutableDictionary alloc] init];
[pixBuffAttributes setObject:
 [NSNumber numberWithInt:kCVPixelFormatType_422YpCbCr8]
 forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
myAsset = [[AVURLAsset alloc] initWithURL:urlpath options:pixBuffAttributes];
myAssetReader = [[AVAssetReader alloc] initWithAsset:myAsset error:nil];
myAssetOutput = [[AVAssetReaderTrackOutput alloc]initWithTrack:
                 [[myAsset tracksWithMediaType:AVMediaTypeVideo]
                 objectAtIndex: 0]
                outputSettings:pixBuffAttributes];
[myAssetReader addOutput:myAssetOutput];
[myAssetReader startReading];
CMSampleBufferRef ref = [myAssetOutput copyNextSampleBuffer];
if(CVBufferGetAttachments(ref, kCVImageBufferFieldDetailKey, nil) == nil)
{
    //always the case
}
else
{
    //never happens
}

regardless of the input file's interlacing the above always returns nil. I'm probably trying to test this in totally the wrong manor, so any help much appreciated!


Solution

  • thanks to jeschot on apple developers for answering this question, https://forums.developer.apple.com/thread/39029 And if anyone else is trying to do something similar the propper way to get most metadata from a video:

        CGSize inputsize = [[myAsset tracksWithMediaType:AVMediaTypeVideo][0] naturalSize];
    
        properties->m_frame_rows = inputsize.height;
        properties->m_pixel_cols = inputsize.width;
    
        CFNumberRef fieldCount = CMFormatDescriptionGetExtension((CMFormatDescriptionRef)[myAsset tracksWithMediaType:AVMediaTypeVideo][0].formatDescriptions[0], kCMFormatDescriptionExtension_FieldCount);
    
        if([(NSNumber*) fieldCount integerValue] == 1)
        {
            properties->m_interlaced = false;
            properties->m_fld2_upper = false;
        }
        else
        {
            properties->m_interlaced = true;
    
            CFPropertyListRef interlace = CMFormatDescriptionGetExtension((CMFormatDescriptionRef)[myAsset tracksWithMediaType:AVMediaTypeVideo][0].formatDescriptions[0], kCMFormatDescriptionExtension_FieldDetail);
    
            if(interlace == kCMFormatDescriptionFieldDetail_SpatialFirstLineEarly)
            {
                properties->m_fld2_upper = false;
            }
    
            if(interlace == kCMFormatDescriptionFieldDetail_SpatialFirstLineLate)
            {
                properties->m_fld2_upper = true;
            }
    
            if(interlace == kCMFormatDescriptionFieldDetail_TemporalBottomFirst)
            {
                properties->m_fld2_upper = true;
            }
    
            if(interlace == kCMFormatDescriptionFieldDetail_TemporalTopFirst)
            {
                properties->m_fld2_upper = false;
            }
        }
    
        CMTime minDuration = [myAsset tracksWithMediaType:AVMediaTypeVideo][0].minFrameDuration;
    
        int64_t fpsNumerator = minDuration.value;
        int32_t fpsDenominator = minDuration.timescale;
    
        properties->m_ticks_duration = (unsigned int) fpsNumerator;
        if (properties->m_interlaced)
        {
            properties->m_ticks_per_second = fpsDenominator * 2;
        }
        else
        {
            properties->m_ticks_per_second = fpsDenominator;
        }
    

    and a quick note for anyone else that gets confused by this, the natural size isn't always the full resolution of the image if the container has metadata such as clean apeture which is less than the full resolution. currently trying to work that one out, but that's a different question!

    UPDATE:

    I've since found that the natural size is the display resolution. to find the encoded resolution you need to decode the first frame and inspect the resolution of the buffer object you get. These can be different in cases such as pixel aspect ratio !=1 or (as described above) clean apeture.