I am using a video renderer which captures an openGL layer (irrlicht engine) using gl_readPixels and outputs a video .mp4 file. This takes place is two stages, the first capturing the video and saving it (AVAssetWriter) and the second taking the saved video and combining it with an audio track (AVMutableCompositionTrack and AVAssetExportSession).
However, when this video is played back (non-quicktime) it appears upside down, despite my attempts to flip it.
I have tried using at the second stage:
AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
// Flip
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, self.frame.size.height);
a_compositionVideoTrack.preferredTransform = flipVertical;
Which works to flip the video on the iPad (plays correctly) but when copying to another device the video still appears upside-down.
I have also tried flipping the AVAssetWriterInput by using:
videoInput.expectsMediaDataInRealTime = YES;
videoInput.transform = CGAffineTransformMakeScale(1, -1);
Which also flips the video correctly on apple devices but not on pc, youtube, facebook. Is there a setting or flag I am missing to define the rotation on a PC?
Edit: I am also using the device (simulator or iPad) in rotation right mode (I was originally using left). When recording the video the home button is on the right hand side. However for my video output using either rotation has no effect on the video rotation.
Edit 2: I have now also tried flipping the view which has the same affect as both changes above.
captureView.transform = CGAffineTransformMakeRotation(180.0f * M_PI / 180.0f)
I've solved my problem by performing a swap operation at the glReadPixels stage. The problem with most of the things I had previously tried is that they tend to just be packaged with the video metadata instead of moving any pixels around. The transform functions especially don't actually do any heavy lifting.
Code included for anyone else who needs to invert their pixels. There might be a better way of doing this using some more optimised code, please share if you know a way!
// OpenGL
unsigned int* pixelBufferData = (unsigned int*)CVPixelBufferGetBaseAddress(pixelBuffer);
glReadPixels(0, 0, self.screen.size.width, self.screen.size.height, GL_BGRA, GL_UNSIGNED_BYTE, pixelBufferData);
unsigned int base;
unsigned int top;
int width = self.screen.size.width;
int height = self.screen.size.height;
int half_height = height / 2;
int base_row_index = height * width;
int top_row_index = 0;
for(int y = 0; y < half_height; y++) {
base_row_index -= width;
top_row_index += width;
for(int x = 0; x < width; x++) {
base = pixelBufferData[top_row_index + x];
top = pixelBufferData[base_row_index + x];
pixelBufferData[top_row_index + x] = top;
pixelBufferData[base_row_index + x] = base;
}
}
Optimised solution using memcpy and two buffers:
for (int row = 0; row < height; ++row){
memcpy(dstBuffer + (height - row - 1) * width * 4, srcBuffer + row * width * 4, width * 4);
}