Project import generated by Copybara.
GitOrigin-RevId: 373e3ac1e5839befd95bf7d73ceff3c5f1171969
This commit is contained in:
@@ -31,6 +31,8 @@
|
||||
CVDisplayLinkRef _videoDisplayLink;
|
||||
#endif // TARGET_OS_OSX
|
||||
id _videoEndObserver;
|
||||
id _audioInterruptedObserver;
|
||||
BOOL _playing;
|
||||
}
|
||||
|
||||
- (instancetype)initWithAVAsset:(AVAsset*)video {
|
||||
@@ -76,12 +78,19 @@
|
||||
usingBlock:^(NSNotification* note) {
|
||||
[weakSelf playerItemDidPlayToEnd:note];
|
||||
}];
|
||||
_audioInterruptedObserver = [center addObserverForName:AVAudioSessionInterruptionNotification
|
||||
object:nil
|
||||
queue:nil
|
||||
usingBlock:^(NSNotification* note) {
|
||||
[weakSelf audioSessionInterruption:note];
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
[_videoPlayer play];
|
||||
_playing = YES;
|
||||
#if !TARGET_OS_OSX
|
||||
_videoDisplayLink.paused = NO;
|
||||
#else
|
||||
@@ -96,6 +105,7 @@
|
||||
CVDisplayLinkStop(_videoDisplayLink);
|
||||
#endif
|
||||
[_videoPlayer pause];
|
||||
_playing = NO;
|
||||
}
|
||||
|
||||
- (BOOL)isRunning {
|
||||
@@ -156,6 +166,17 @@ static CVReturn renderCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
|
||||
});
|
||||
}
|
||||
|
||||
- (void)audioSessionInterruption:(NSNotification*)notification {
|
||||
if ([notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue] ==
|
||||
AVAudioSessionInterruptionTypeEnded) {
|
||||
if ([notification.userInfo[AVAudioSessionInterruptionOptionKey] intValue] ==
|
||||
AVAudioSessionInterruptionOptionShouldResume && _playing) {
|
||||
// AVVideoPlayer does not automatically resume on this notification.
|
||||
[_videoPlayer play];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)seekToTime:(CMTime)time tolerance:(CMTime)tolerance {
|
||||
[_videoPlayer seekToTime:time toleranceBefore:tolerance toleranceAfter:tolerance];
|
||||
}
|
||||
|
||||
+118
-7
@@ -241,6 +241,44 @@ vImage_Error vImageConvertCVPixelBuffers(CVPixelBufferRef src,
|
||||
return error;
|
||||
}
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
static void FreeRefConReleaseCallback(void* refCon, const void* baseAddress) {
|
||||
free(refCon);
|
||||
}
|
||||
#endif
|
||||
|
||||
CVReturn CreateCVPixelBufferWithoutPool(int width, int height, OSType cv_format,
|
||||
CVPixelBufferRef* out_buffer) {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
// On the simulator, syncing the texture with the pixelbuffer does not work,
|
||||
// and we have to use glReadPixels. Since GL_UNPACK_ROW_LENGTH is not
|
||||
// available in OpenGL ES 2, we should create the buffer so the pixels are
|
||||
// contiguous.
|
||||
//
|
||||
// TODO: verify if we can use kIOSurfaceBytesPerRow to force
|
||||
// CoreVideo to give us contiguous data.
|
||||
size_t bytes_per_row = width * 4;
|
||||
void* data = malloc(bytes_per_row * height);
|
||||
return CVPixelBufferCreateWithBytes(
|
||||
kCFAllocatorDefault, width, height, cv_format, data, bytes_per_row,
|
||||
FreeRefConReleaseCallback, data,
|
||||
GetCVPixelBufferAttributesForGlCompatibility(), out_buffer);
|
||||
#else
|
||||
return CVPixelBufferCreate(kCFAllocatorDefault, width, height, cv_format,
|
||||
GetCVPixelBufferAttributesForGlCompatibility(),
|
||||
out_buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
absl::StatusOr<CFHolder<CVPixelBufferRef>> CreateCVPixelBufferWithoutPool(
|
||||
int width, int height, OSType cv_format) {
|
||||
CVPixelBufferRef buffer;
|
||||
CVReturn err =
|
||||
CreateCVPixelBufferWithoutPool(width, height, cv_format, &buffer);
|
||||
RET_CHECK(err == kCVReturnSuccess) << "Error creating pixel buffer: " << err;
|
||||
return MakeCFHolderAdopting(buffer);
|
||||
}
|
||||
|
||||
void ReleaseMediaPipePacket(void* refcon, const void* base_address) {
|
||||
auto packet = (mediapipe::Packet*)refcon;
|
||||
delete packet;
|
||||
@@ -288,13 +326,9 @@ absl::Status CreateCVPixelBufferForImageFramePacket(
|
||||
if (can_overwrite) {
|
||||
v_dest = v_image;
|
||||
} else {
|
||||
CVPixelBufferRef pixel_buffer_temp;
|
||||
status = CVPixelBufferCreate(
|
||||
kCFAllocatorDefault, frame.Width(), frame.Height(), pixel_format,
|
||||
GetCVPixelBufferAttributesForGlCompatibility(), &pixel_buffer_temp);
|
||||
RET_CHECK(status == kCVReturnSuccess)
|
||||
<< "CVPixelBufferCreate failed: " << status;
|
||||
pixel_buffer.adopt(pixel_buffer_temp);
|
||||
ASSIGN_OR_RETURN(pixel_buffer,
|
||||
CreateCVPixelBufferWithoutPool(
|
||||
frame.Width(), frame.Height(), pixel_format));
|
||||
status = CVPixelBufferLockBaseAddress(*pixel_buffer,
|
||||
kCVPixelBufferLock_ReadOnly);
|
||||
RET_CHECK(status == kCVReturnSuccess)
|
||||
@@ -345,6 +379,83 @@ absl::Status CreateCVPixelBufferForImageFramePacket(
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::StatusOr<CFHolder<CVPixelBufferRef>> CreateCVPixelBufferCopyingImageFrame(
|
||||
const mediapipe::ImageFrame& image_frame) {
|
||||
CFHolder<CVPixelBufferRef> pixel_buffer;
|
||||
OSType pixel_format = 0;
|
||||
std::function<absl::Status(const vImage_Buffer&, vImage_Buffer&)> copy_fun =
|
||||
[](const vImage_Buffer& src, vImage_Buffer& dst) -> absl::Status {
|
||||
const char* src_row = reinterpret_cast<const char*>(src.data);
|
||||
char* dst_row = reinterpret_cast<char*>(dst.data);
|
||||
if (src.rowBytes == dst.rowBytes) {
|
||||
memcpy(dst_row, src_row, src.height * src.rowBytes);
|
||||
} else {
|
||||
for (int i = src.height; i > 0; --i) {
|
||||
memcpy(dst_row, src_row, src.rowBytes);
|
||||
src_row += src.rowBytes;
|
||||
dst_row += dst.rowBytes;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
// TODO: unify some code with CreateCVPixelBufferForImageFramePacket?
|
||||
mediapipe::ImageFormat::Format image_format = image_frame.Format();
|
||||
switch (image_format) {
|
||||
case mediapipe::ImageFormat::SRGBA:
|
||||
pixel_format = kCVPixelFormatType_32BGRA;
|
||||
copy_fun = [](const vImage_Buffer& src,
|
||||
vImage_Buffer& dst) -> absl::Status {
|
||||
// Swap R and B channels.
|
||||
const uint8_t permute_map[4] = {2, 1, 0, 3};
|
||||
vImage_Error vError = vImagePermuteChannels_ARGB8888(
|
||||
&src, &dst, permute_map, kvImageNoFlags);
|
||||
RET_CHECK(vError == kvImageNoError)
|
||||
<< "vImagePermuteChannels failed: " << vError;
|
||||
return {};
|
||||
};
|
||||
break;
|
||||
|
||||
case mediapipe::ImageFormat::GRAY8:
|
||||
pixel_format = kCVPixelFormatType_OneComponent8;
|
||||
break;
|
||||
|
||||
case mediapipe::ImageFormat::VEC32F1:
|
||||
pixel_format = kCVPixelFormatType_OneComponent32Float;
|
||||
break;
|
||||
|
||||
case mediapipe::ImageFormat::VEC32F2:
|
||||
pixel_format = kCVPixelFormatType_TwoComponent32Float;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ::mediapipe::UnknownErrorBuilder(MEDIAPIPE_LOC)
|
||||
<< "unsupported ImageFrame format: " << image_format;
|
||||
}
|
||||
|
||||
CVReturn cv_err;
|
||||
ASSIGN_OR_RETURN(pixel_buffer, CreateCVPixelBufferWithoutPool(
|
||||
image_frame.Width(), image_frame.Height(),
|
||||
pixel_format));
|
||||
cv_err =
|
||||
CVPixelBufferLockBaseAddress(*pixel_buffer, kCVPixelBufferLock_ReadOnly);
|
||||
RET_CHECK(cv_err == kCVReturnSuccess)
|
||||
<< "CVPixelBufferLockBaseAddress failed: " << cv_err;
|
||||
|
||||
vImage_Buffer v_image = vImageForImageFrame(image_frame);
|
||||
vImage_Buffer v_dest = vImageForCVPixelBuffer(*pixel_buffer);
|
||||
auto status = copy_fun(v_image, v_dest);
|
||||
|
||||
cv_err = CVPixelBufferUnlockBaseAddress(*pixel_buffer,
|
||||
kCVPixelBufferLock_ReadOnly);
|
||||
RET_CHECK(cv_err == kCVReturnSuccess)
|
||||
<< "CVPixelBufferUnlockBaseAddress failed: " << cv_err;
|
||||
|
||||
MP_RETURN_IF_ERROR(status);
|
||||
|
||||
return pixel_buffer;
|
||||
}
|
||||
|
||||
absl::Status CreateCGImageFromCVPixelBuffer(CVPixelBufferRef image_buffer,
|
||||
CFHolder<CGImageRef>* image) {
|
||||
CVReturn status =
|
||||
|
||||
@@ -63,6 +63,13 @@ vImage_Error vImageConvertCVPixelBuffers(CVPixelBufferRef src,
|
||||
/// alive while the CVPixelBuffer is in use.
|
||||
void ReleaseMediaPipePacket(void* refcon, const void* base_address);
|
||||
|
||||
// Create a CVPixelBuffer without using a pool. See pixel_buffer_pool_util.h
|
||||
// for creation functions that use pools.
|
||||
CVReturn CreateCVPixelBufferWithoutPool(int width, int height, OSType cv_format,
|
||||
CVPixelBufferRef* out_buffer);
|
||||
absl::StatusOr<CFHolder<CVPixelBufferRef>> CreateCVPixelBufferWithoutPool(
|
||||
int width, int height, OSType cv_format);
|
||||
|
||||
/// Returns a CVPixelBuffer that references the data inside the packet. The
|
||||
/// packet must contain an ImageFrame. The CVPixelBuffer manages a copy of
|
||||
/// the packet, so that the packet's data is kept alive as long as the
|
||||
@@ -78,6 +85,8 @@ absl::Status CreateCVPixelBufferForImageFramePacket(
|
||||
absl::Status CreateCVPixelBufferForImageFramePacket(
|
||||
const mediapipe::Packet& image_frame_packet, bool can_overwrite,
|
||||
CFHolder<CVPixelBufferRef>* out_buffer);
|
||||
absl::StatusOr<CFHolder<CVPixelBufferRef>> CreateCVPixelBufferCopyingImageFrame(
|
||||
const mediapipe::ImageFrame& image_frame);
|
||||
|
||||
/// Creates a CVPixelBuffer with a copy of the contents of the CGImage.
|
||||
absl::Status CreateCVPixelBufferFromCGImage(
|
||||
|
||||
Reference in New Issue
Block a user