312 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			312 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#include <kinc/video.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#import <AVFoundation/AVFoundation.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/audio1/audio.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/graphics4/texture.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/io/filereader.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/log.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/system.h>
							 | 
						||
| 
								 | 
							
								#include <stdio.h>
							 | 
						||
| 
								 | 
							
								#include <stdlib.h>
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								extern const char *iphonegetresourcepath(void);
							 | 
						||
| 
								 | 
							
								extern const char *macgetresourcepath(void);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {
							 | 
						||
| 
								 | 
							
									stream->bufferSize = 1024 * 100;
							 | 
						||
| 
								 | 
							
									stream->bufferReadPosition = 0;
							 | 
						||
| 
								 | 
							
									stream->bufferWritePosition = 0;
							 | 
						||
| 
								 | 
							
									stream->read = 0;
							 | 
						||
| 
								 | 
							
									stream->written = 0;
							 | 
						||
| 
								 | 
							
									stream->buffer = (float *)malloc(stream->bufferSize * sizeof(float));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {
							 | 
						||
| 
								 | 
							
									free(stream->buffer);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {
							 | 
						||
| 
								 | 
							
									for (int i = 0; i < sample_count; ++i) {
							 | 
						||
| 
								 | 
							
										float value = data[i]; // / 32767.0;
							 | 
						||
| 
								 | 
							
										stream->buffer[stream->bufferWritePosition++] = value;
							 | 
						||
| 
								 | 
							
										++stream->written;
							 | 
						||
| 
								 | 
							
										if (stream->bufferWritePosition >= stream->bufferSize) {
							 | 
						||
| 
								 | 
							
											stream->bufferWritePosition = 0;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static float samples[2] = {0};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) {
							 | 
						||
| 
								 | 
							
									++stream->read;
							 | 
						||
| 
								 | 
							
									if (stream->written <= stream->read) {
							 | 
						||
| 
								 | 
							
										kinc_log(KINC_LOG_LEVEL_WARNING, "Out of audio\n");
							 | 
						||
| 
								 | 
							
										return 0;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (stream->bufferReadPosition >= stream->bufferSize) {
							 | 
						||
| 
								 | 
							
										stream->bufferReadPosition = 0;
							 | 
						||
| 
								 | 
							
										kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									samples[0] = stream->buffer[stream->bufferReadPosition++];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (stream->bufferReadPosition >= stream->bufferSize) {
							 | 
						||
| 
								 | 
							
										stream->bufferReadPosition = 0;
							 | 
						||
| 
								 | 
							
										kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									samples[1] = stream->buffer[stream->bufferReadPosition++];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return samples;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) {
							 | 
						||
| 
								 | 
							
									return false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void load(kinc_video_t *video, double startTime) {
							 | 
						||
| 
								 | 
							
									video->impl.videoStart = startTime;
							 | 
						||
| 
								 | 
							
									AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:video->impl.url options:nil];
							 | 
						||
| 
								 | 
							
									video->impl.videoAsset = asset;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									video->impl.duration = [asset duration].value / [asset duration].timescale;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
							 | 
						||
| 
								 | 
							
									NSDictionary *videoOutputSettings =
							 | 
						||
| 
								 | 
							
									    [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil];
							 | 
						||
| 
								 | 
							
									AVAssetReaderTrackOutput *videoOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:videoOutputSettings];
							 | 
						||
| 
								 | 
							
									[videoOutput setSupportsRandomAccess:YES];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									bool hasAudio = [[asset tracksWithMediaType:AVMediaTypeAudio] count] > 0;
							 | 
						||
| 
								 | 
							
									AVAssetReaderAudioMixOutput *audioOutput = NULL;
							 | 
						||
| 
								 | 
							
									if (hasAudio) {
							 | 
						||
| 
								 | 
							
										AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
							 | 
						||
| 
								 | 
							
										NSDictionary *audioOutputSettings = [NSDictionary
							 | 
						||
| 
								 | 
							
										    dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey,
							 | 
						||
| 
								 | 
							
										                                 [NSNumber numberWithInt:32], AVLinearPCMBitDepthKey, [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
							 | 
						||
| 
								 | 
							
										                                 [NSNumber numberWithBool:YES], AVLinearPCMIsFloatKey, [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, nil];
							 | 
						||
| 
								 | 
							
										audioOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:@[ audioTrack ] audioSettings:audioOutputSettings];
							 | 
						||
| 
								 | 
							
										[audioOutput setSupportsRandomAccess:YES];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:asset error:nil];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (startTime > 0) {
							 | 
						||
| 
								 | 
							
										CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startTime * 1000, 1000), kCMTimePositiveInfinity);
							 | 
						||
| 
								 | 
							
										reader.timeRange = timeRange;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									[reader addOutput:videoOutput];
							 | 
						||
| 
								 | 
							
									if (hasAudio) {
							 | 
						||
| 
								 | 
							
										[reader addOutput:audioOutput];
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									video->impl.assetReader = reader;
							 | 
						||
| 
								 | 
							
									video->impl.videoTrackOutput = videoOutput;
							 | 
						||
| 
								 | 
							
									if (hasAudio) {
							 | 
						||
| 
								 | 
							
										video->impl.audioTrackOutput = audioOutput;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else {
							 | 
						||
| 
								 | 
							
										video->impl.audioTrackOutput = NULL;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (video->impl.myWidth < 0)
							 | 
						||
| 
								 | 
							
										video->impl.myWidth = [videoTrack naturalSize].width;
							 | 
						||
| 
								 | 
							
									if (video->impl.myHeight < 0)
							 | 
						||
| 
								 | 
							
										video->impl.myHeight = [videoTrack naturalSize].height;
							 | 
						||
| 
								 | 
							
									int framerate = [videoTrack nominalFrameRate];
							 | 
						||
| 
								 | 
							
									kinc_log(KINC_LOG_LEVEL_INFO, "Framerate: %i\n", framerate);
							 | 
						||
| 
								 | 
							
									video->impl.next = video->impl.videoStart;
							 | 
						||
| 
								 | 
							
									video->impl.audioTime = video->impl.videoStart * 44100;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_video_init(kinc_video_t *video, const char *filename) {
							 | 
						||
| 
								 | 
							
									video->impl.playing = false;
							 | 
						||
| 
								 | 
							
									video->impl.sound = NULL;
							 | 
						||
| 
								 | 
							
									video->impl.image_initialized = false;
							 | 
						||
| 
								 | 
							
									char name[2048];
							 | 
						||
| 
								 | 
							
								#ifdef KINC_IOS
							 | 
						||
| 
								 | 
							
									strcpy(name, iphonegetresourcepath());
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
									strcpy(name, macgetresourcepath());
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
									strcat(name, "/");
							 | 
						||
| 
								 | 
							
									strcat(name, KINC_DEBUGDIR);
							 | 
						||
| 
								 | 
							
									strcat(name, "/");
							 | 
						||
| 
								 | 
							
									strcat(name, filename);
							 | 
						||
| 
								 | 
							
									video->impl.url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:name]];
							 | 
						||
| 
								 | 
							
									video->impl.myWidth = -1;
							 | 
						||
| 
								 | 
							
									video->impl.myHeight = -1;
							 | 
						||
| 
								 | 
							
									video->impl.finished = false;
							 | 
						||
| 
								 | 
							
									video->impl.duration = 0;
							 | 
						||
| 
								 | 
							
									load(video, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_video_destroy(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									kinc_video_stop(video);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef KINC_IOS
							 | 
						||
| 
								 | 
							
								void iosPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video);
							 | 
						||
| 
								 | 
							
								void iosStopVideoSoundStream(void);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								void macPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video);
							 | 
						||
| 
								 | 
							
								void macStopVideoSoundStream(void);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_video_play(kinc_video_t *video, bool loop) {
							 | 
						||
| 
								 | 
							
									AVAssetReader *reader = video->impl.assetReader;
							 | 
						||
| 
								 | 
							
									[reader startReading];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									kinc_internal_video_sound_stream_t *stream = (kinc_internal_video_sound_stream_t *)malloc(sizeof(kinc_internal_video_sound_stream_t));
							 | 
						||
| 
								 | 
							
									kinc_internal_video_sound_stream_init(stream, 2, 44100);
							 | 
						||
| 
								 | 
							
									video->impl.sound = stream;
							 | 
						||
| 
								 | 
							
								#ifdef KINC_IOS
							 | 
						||
| 
								 | 
							
									iosPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
									macPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									video->impl.playing = true;
							 | 
						||
| 
								 | 
							
									video->impl.start = kinc_time() - video->impl.videoStart;
							 | 
						||
| 
								 | 
							
									video->impl.loop = loop;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_video_pause(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									video->impl.playing = false;
							 | 
						||
| 
								 | 
							
									if (video->impl.sound != NULL) {
							 | 
						||
| 
								 | 
							
								// Mixer::stop(sound);
							 | 
						||
| 
								 | 
							
								#ifdef KINC_IOS
							 | 
						||
| 
								 | 
							
										iosStopVideoSoundStream();
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
										macStopVideoSoundStream();
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
										kinc_internal_video_sound_stream_destroy((kinc_internal_video_sound_stream_t *)video->impl.sound);
							 | 
						||
| 
								 | 
							
										free(video->impl.sound);
							 | 
						||
| 
								 | 
							
										video->impl.sound = NULL;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_video_stop(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									kinc_video_pause(video);
							 | 
						||
| 
								 | 
							
									video->impl.finished = true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void updateImage(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									if (!video->impl.playing)
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									{
							 | 
						||
| 
								 | 
							
										AVAssetReaderTrackOutput *videoOutput = video->impl.videoTrackOutput;
							 | 
						||
| 
								 | 
							
										CMSampleBufferRef buffer = [videoOutput copyNextSampleBuffer];
							 | 
						||
| 
								 | 
							
										if (!buffer) {
							 | 
						||
| 
								 | 
							
											if (video->impl.loop) {
							 | 
						||
| 
								 | 
							
												CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(0, 1000), kCMTimePositiveInfinity);
							 | 
						||
| 
								 | 
							
												[videoOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput;
							 | 
						||
| 
								 | 
							
												CMSampleBufferRef audio_buffer = [audioOutput copyNextSampleBuffer];
							 | 
						||
| 
								 | 
							
												while (audio_buffer) {
							 | 
						||
| 
								 | 
							
													audio_buffer = [audioOutput copyNextSampleBuffer];
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												[audioOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												buffer = [videoOutput copyNextSampleBuffer];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												video->impl.start = kinc_time() - video->impl.videoStart;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											else {
							 | 
						||
| 
								 | 
							
												kinc_video_stop(video);
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										video->impl.next = CMTimeGetSeconds(CMSampleBufferGetOutputPresentationTimeStamp(buffer));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(buffer);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (!video->impl.image_initialized) {
							 | 
						||
| 
								 | 
							
											CGSize size = CVImageBufferGetDisplaySize(pixelBuffer);
							 | 
						||
| 
								 | 
							
											video->impl.myWidth = size.width;
							 | 
						||
| 
								 | 
							
											video->impl.myHeight = size.height;
							 | 
						||
| 
								 | 
							
											kinc_g4_texture_init(&video->impl.image, kinc_video_width(video), kinc_video_height(video), KINC_IMAGE_FORMAT_BGRA32);
							 | 
						||
| 
								 | 
							
											video->impl.image_initialized = true;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (pixelBuffer != NULL) {
							 | 
						||
| 
								 | 
							
											CVPixelBufferLockBaseAddress(pixelBuffer, 0);
							 | 
						||
| 
								 | 
							
								#ifdef KINC_OPENGL
							 | 
						||
| 
								 | 
							
											kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer),
							 | 
						||
| 
								 | 
							
											                       (int)(CVPixelBufferGetBytesPerRow(pixelBuffer) / 4));
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
											kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer), (int)(CVPixelBufferGetBytesPerRow(pixelBuffer)));
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
											CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										CFRelease(buffer);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (video->impl.audioTrackOutput != NULL) {
							 | 
						||
| 
								 | 
							
										AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput;
							 | 
						||
| 
								 | 
							
										while (video->impl.audioTime / 44100.0 < video->impl.next + 0.1) {
							 | 
						||
| 
								 | 
							
											CMSampleBufferRef buffer = [audioOutput copyNextSampleBuffer];
							 | 
						||
| 
								 | 
							
											if (!buffer)
							 | 
						||
| 
								 | 
							
												return;
							 | 
						||
| 
								 | 
							
											CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(buffer);
							 | 
						||
| 
								 | 
							
											AudioBufferList audioBufferList;
							 | 
						||
| 
								 | 
							
											CMBlockBufferRef blockBufferOut = nil;
							 | 
						||
| 
								 | 
							
											CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL,
							 | 
						||
| 
								 | 
							
											                                                        kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBufferOut);
							 | 
						||
| 
								 | 
							
											for (int bufferCount = 0; bufferCount < audioBufferList.mNumberBuffers; ++bufferCount) {
							 | 
						||
| 
								 | 
							
												float *samples = (float *)audioBufferList.mBuffers[bufferCount].mData;
							 | 
						||
| 
								 | 
							
												kinc_internal_video_sound_stream_t *sound = (kinc_internal_video_sound_stream_t *)video->impl.sound;
							 | 
						||
| 
								 | 
							
												if (video->impl.audioTime / 44100.0 > video->impl.next - 0.1) {
							 | 
						||
| 
								 | 
							
													kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer * 2);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												else {
							 | 
						||
| 
								 | 
							
													// Send some data anyway because the buffers are huge
							 | 
						||
| 
								 | 
							
													kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer);
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												video->impl.audioTime += numSamplesInBuffer;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											CFRelease(blockBufferOut);
							 | 
						||
| 
								 | 
							
											CFRelease(buffer);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_video_update(kinc_video_t *video, double time) {
							 | 
						||
| 
								 | 
							
									if (video->impl.playing && time >= video->impl.start + video->impl.next) {
							 | 
						||
| 
								 | 
							
										updateImage(video);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int kinc_video_width(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									return video->impl.myWidth;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int kinc_video_height(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									return video->impl.myHeight;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									kinc_video_update(video, kinc_time());
							 | 
						||
| 
								 | 
							
									return &video->impl.image;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								double kinc_video_duration(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									return video->impl.duration;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool kinc_video_finished(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									return video->impl.finished;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool kinc_video_paused(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									return !video->impl.playing;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								double kinc_video_position(kinc_video_t *video) {
							 | 
						||
| 
								 | 
							
									return video->impl.next - video->impl.start;
							 | 
						||
| 
								 | 
							
								}
							 |