mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-06-16 21:50:57 +00:00
Rip out the old video renderer and decoder
This commit is contained in:
+3
-15
@@ -12,8 +12,6 @@
|
||||
|
||||
#include "Limelight.h"
|
||||
#include "opus.h"
|
||||
#include "VideoDecoder.h"
|
||||
#include "VideoRenderer.h"
|
||||
|
||||
@implementation Connection {
|
||||
IP_ADDRESS host;
|
||||
@@ -39,15 +37,13 @@ bool started = false;
|
||||
void DrSetup(int width, int height, int fps, void* context, int drFlags)
|
||||
{
|
||||
printf("Setup video\n");
|
||||
nv_avc_init(width, height, DISABLE_LOOP_FILTER | FAST_DECODE | FAST_BILINEAR_FILTERING, 2);
|
||||
}
|
||||
|
||||
void DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
|
||||
{
|
||||
unsigned char* data = (unsigned char*) malloc(decodeUnit->fullLength + nv_avc_get_input_padding_size());
|
||||
unsigned char* data = (unsigned char*) malloc(decodeUnit->fullLength);
|
||||
if (data != NULL) {
|
||||
int offset = 0;
|
||||
int err;
|
||||
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
while (entry != NULL) {
|
||||
@@ -56,10 +52,7 @@ void DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
err = nv_avc_decode(data, decodeUnit->fullLength);
|
||||
if (err != 0) {
|
||||
printf("Decode failed: %d\n", err);
|
||||
}
|
||||
// FIXME: Submit data to decoder
|
||||
|
||||
free(data);
|
||||
}
|
||||
@@ -68,19 +61,16 @@ void DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
|
||||
void DrStart(void)
|
||||
{
|
||||
printf("Start video\n");
|
||||
[VideoRenderer startRendering];
|
||||
}
|
||||
|
||||
void DrStop(void)
|
||||
{
|
||||
printf("Stop video\n");
|
||||
[VideoRenderer stopRendering];
|
||||
}
|
||||
|
||||
void DrRelease(void)
|
||||
{
|
||||
printf("Release video\n");
|
||||
nv_avc_destroy();
|
||||
}
|
||||
|
||||
void ArInit(void)
|
||||
@@ -131,9 +121,7 @@ void ArDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||
// Return of opus_decode is samples per channel
|
||||
filledPcmBuffer *= 4;
|
||||
|
||||
NSLog(@"pcmBuffer: %d", filledPcmBuffer);
|
||||
//[audioRendererBlock lock];
|
||||
|
||||
NSLog(@"pcmBuffer: %d", filledPcmBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
#import "MainFrameViewController.h"
|
||||
#import "VideoDepacketizer.h"
|
||||
#import "ConnectionHandler.h"
|
||||
#import "Computer.h"
|
||||
#import "CryptoManager.h"
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
|
||||
#import "StreamFrameViewController.h"
|
||||
#import "MainFrameViewController.h"
|
||||
#import "VideoDepacketizer.h"
|
||||
#import "Connection.h"
|
||||
#import "VideoRenderer.h"
|
||||
#import "VideoDecoderRenderer.h"
|
||||
#import "ConnectionHandler.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
@@ -48,8 +47,7 @@
|
||||
|
||||
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
|
||||
[opQueue addOperation:conn];
|
||||
[opQueue addOperation:[[VideoRenderer alloc]initWithTarget:streamView]];
|
||||
|
||||
[opQueue addOperation:[[VideoDecoderRenderer alloc]initWithTarget:streamView]];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
//
|
||||
|
||||
#import "StreamView.h"
|
||||
#import "VideoDecoder.h"
|
||||
#import "VideoRenderer.h"
|
||||
|
||||
@implementation StreamView {
|
||||
size_t width;
|
||||
@@ -43,14 +41,9 @@
|
||||
// An empty implementation adversely affects performance during animation.
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
if (![VideoRenderer isRendering]) {
|
||||
/*if (![VideoRenderer isRendering]) {
|
||||
return;
|
||||
}
|
||||
if (!nv_avc_get_rgb_frame((char*)pixelData, width*height*4))
|
||||
{
|
||||
//NSLog(@"no new decoded frame!");
|
||||
//return;
|
||||
}
|
||||
}*/
|
||||
|
||||
bitmapContext = CGBitmapContextCreate(pixelData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
|
||||
image = CGBitmapContextCreateImage(bitmapContext);
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// VideoDecoder.h
|
||||
// Limelight-iOS
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/18/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Disables the deblocking filter at the cost of image quality
|
||||
#define DISABLE_LOOP_FILTER 0x1
|
||||
// Uses the low latency decode flag (disables multithreading)
|
||||
#define LOW_LATENCY_DECODE 0x2
|
||||
// Threads process each slice, rather than each frame
|
||||
#define SLICE_THREADING 0x4
|
||||
// Uses nonstandard speedup tricks
|
||||
#define FAST_DECODE 0x8
|
||||
// Uses bilinear filtering instead of bicubic
|
||||
#define BILINEAR_FILTERING 0x10
|
||||
// Uses a faster bilinear filtering with lower image quality
|
||||
#define FAST_BILINEAR_FILTERING 0x20
|
||||
// Disables color conversion (output is NV21)
|
||||
#define NO_COLOR_CONVERSION 0x40
|
||||
// Native color format: RGB0
|
||||
#define NATIVE_COLOR_RGB0 0x80
|
||||
// Native color format: 0RGB
|
||||
#define NATIVE_COLOR_0RGB 0x100
|
||||
// Native color format: ARGB
|
||||
#define NATIVE_COLOR_ARGB 0x200
|
||||
// Native color format: RGBA
|
||||
#define NATIVE_COLOR_RGBA 0x400
|
||||
|
||||
@interface VideoDecoder : NSObject
|
||||
int nv_avc_init(int width, int height, int perf_lvl, int thread_count);
|
||||
void nv_avc_destroy(void);
|
||||
|
||||
int nv_avc_get_raw_frame(char* buffer, int size);
|
||||
|
||||
int nv_avc_get_rgb_frame(char* buffer, int size);
|
||||
int nv_avc_get_rgb_frame_int(int* buffer, int size);
|
||||
int nv_avc_redraw(void);
|
||||
|
||||
int nv_avc_get_input_padding_size(void);
|
||||
int nv_avc_decode(unsigned char* indata, int inlen);
|
||||
@end
|
||||
@@ -1,337 +0,0 @@
|
||||
//
|
||||
// VideoDecoder.m
|
||||
// Limelight-iOS
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/18/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VideoDecoder.h"
|
||||
#import "avcodec.h"
|
||||
#import "swscale.h"
|
||||
#include <pthread.h>
|
||||
|
||||
@implementation VideoDecoder
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
return self;
|
||||
}
|
||||
|
||||
// General decoder and renderer state
|
||||
AVPacket pkt;
|
||||
AVCodec* decoder;
|
||||
AVCodecContext* decoder_ctx;
|
||||
AVFrame* yuv_frame;
|
||||
AVFrame* dec_frame;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
// Color conversion and rendering
|
||||
AVFrame* rgb_frame;
|
||||
char* rgb_frame_buf;
|
||||
struct SwsContext* scaler_ctx;
|
||||
int render_pix_fmt;
|
||||
|
||||
#define BYTES_PER_PIXEL 4
|
||||
|
||||
// This function must be called before
|
||||
// any other decoding functions
|
||||
int nv_avc_init(int width, int height, int perf_lvl, int thread_count) {
|
||||
int err;
|
||||
int filtering;
|
||||
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
|
||||
// Initialize the avcodec library and register codecs
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
avcodec_register_all();
|
||||
|
||||
av_init_packet(&pkt);
|
||||
|
||||
decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
|
||||
if (decoder == NULL) {
|
||||
// __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't find H264 decoder");
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder_ctx = avcodec_alloc_context3(decoder);
|
||||
if (decoder_ctx == NULL) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't allocate context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Show frames even before a reference frame
|
||||
decoder_ctx->flags2 |= CODEC_FLAG2_SHOW_ALL;
|
||||
|
||||
if (perf_lvl & DISABLE_LOOP_FILTER) {
|
||||
// Skip the loop filter for performance reasons
|
||||
decoder_ctx->skip_loop_filter = AVDISCARD_ALL;
|
||||
}
|
||||
|
||||
if (perf_lvl & LOW_LATENCY_DECODE) {
|
||||
// Use low delay single threaded encoding
|
||||
decoder_ctx->flags |= CODEC_FLAG_LOW_DELAY;
|
||||
}
|
||||
|
||||
if (perf_lvl & SLICE_THREADING) {
|
||||
decoder_ctx->thread_type = FF_THREAD_SLICE;
|
||||
}
|
||||
else {
|
||||
decoder_ctx->thread_type = FF_THREAD_FRAME;
|
||||
}
|
||||
|
||||
decoder_ctx->thread_count = thread_count;
|
||||
|
||||
decoder_ctx->width = width;
|
||||
decoder_ctx->height = height;
|
||||
decoder_ctx->pix_fmt = PIX_FMT_YUV420P;
|
||||
|
||||
render_pix_fmt = AV_PIX_FMT_BGR0;
|
||||
|
||||
err = avcodec_open2(decoder_ctx, decoder, NULL);
|
||||
if (err < 0) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't open codec");
|
||||
return err;
|
||||
}
|
||||
|
||||
dec_frame = av_frame_alloc();
|
||||
if (dec_frame == NULL) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't allocate frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(perf_lvl & NO_COLOR_CONVERSION)) {
|
||||
rgb_frame = av_frame_alloc();
|
||||
if (rgb_frame == NULL) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't allocate frame");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rgb_frame_buf = (char*)av_malloc(width * height * BYTES_PER_PIXEL);
|
||||
if (rgb_frame_buf == NULL) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't allocate picture");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = avpicture_fill((AVPicture*)rgb_frame,
|
||||
(unsigned char*)rgb_frame_buf,
|
||||
render_pix_fmt,
|
||||
decoder_ctx->width,
|
||||
decoder_ctx->height);
|
||||
if (err < 0) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't fill picture");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (perf_lvl & FAST_BILINEAR_FILTERING) {
|
||||
filtering = SWS_FAST_BILINEAR;
|
||||
}
|
||||
else if (perf_lvl & BILINEAR_FILTERING) {
|
||||
filtering = SWS_BILINEAR;
|
||||
}
|
||||
else {
|
||||
filtering = SWS_BICUBIC;
|
||||
}
|
||||
|
||||
scaler_ctx = sws_getContext(decoder_ctx->width,
|
||||
decoder_ctx->height,
|
||||
decoder_ctx->pix_fmt,
|
||||
decoder_ctx->width,
|
||||
decoder_ctx->height,
|
||||
render_pix_fmt,
|
||||
filtering,
|
||||
NULL, NULL, NULL);
|
||||
if (scaler_ctx == NULL) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Couldn't get scaler context");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This function must be called after
|
||||
// decoding is finished
|
||||
void nv_avc_destroy(void) {
|
||||
if (decoder_ctx) {
|
||||
avcodec_close(decoder_ctx);
|
||||
av_free(decoder_ctx);
|
||||
decoder_ctx = NULL;
|
||||
}
|
||||
if (scaler_ctx) {
|
||||
sws_freeContext(scaler_ctx);
|
||||
scaler_ctx = NULL;
|
||||
}
|
||||
if (dec_frame) {
|
||||
av_frame_free(&dec_frame);
|
||||
dec_frame = NULL;
|
||||
}
|
||||
if (yuv_frame) {
|
||||
av_frame_free(&yuv_frame);
|
||||
yuv_frame = NULL;
|
||||
}
|
||||
if (rgb_frame) {
|
||||
av_frame_free(&rgb_frame);
|
||||
rgb_frame = NULL;
|
||||
}
|
||||
if (rgb_frame_buf) {
|
||||
av_free(rgb_frame_buf);
|
||||
rgb_frame_buf = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
static AVFrame* dequeue_new_frame(void) {
|
||||
AVFrame *our_yuv_frame = NULL;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
// Check if there's a new frame
|
||||
if (yuv_frame) {
|
||||
// We now own the decoder's frame and are
|
||||
// responsible for freeing it when we're done
|
||||
our_yuv_frame = yuv_frame;
|
||||
yuv_frame = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
return our_yuv_frame;
|
||||
}
|
||||
|
||||
static int update_rgb_frame(void) {
|
||||
AVFrame *our_yuv_frame;
|
||||
int err;
|
||||
|
||||
our_yuv_frame = dequeue_new_frame();
|
||||
if (our_yuv_frame == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const unsigned char* data = (unsigned char*)our_yuv_frame->data;
|
||||
|
||||
// Convert the YUV image to RGB
|
||||
err = sws_scale(scaler_ctx,
|
||||
&data,
|
||||
our_yuv_frame->linesize,
|
||||
0,
|
||||
decoder_ctx->height,
|
||||
rgb_frame->data,
|
||||
rgb_frame->linesize);
|
||||
|
||||
av_frame_free(&our_yuv_frame);
|
||||
|
||||
if (err != decoder_ctx->height) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Scaling failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int render_rgb_to_buffer(char* buffer, int size) {
|
||||
int err;
|
||||
|
||||
// Draw the frame to the buffer
|
||||
err = avpicture_layout((AVPicture*)rgb_frame,
|
||||
render_pix_fmt,
|
||||
decoder_ctx->width,
|
||||
decoder_ctx->height,
|
||||
(unsigned char*)buffer,
|
||||
size);
|
||||
if (err < 0) {
|
||||
// __android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Picture fill failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int nv_avc_get_raw_frame(char* buffer, int size) {
|
||||
AVFrame *our_yuv_frame;
|
||||
int err;
|
||||
|
||||
our_yuv_frame = dequeue_new_frame();
|
||||
if (our_yuv_frame == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = avpicture_layout((AVPicture*)our_yuv_frame,
|
||||
decoder_ctx->pix_fmt,
|
||||
decoder_ctx->width,
|
||||
decoder_ctx->height,
|
||||
(unsigned char*)buffer,
|
||||
size);
|
||||
|
||||
av_frame_free(&our_yuv_frame);
|
||||
|
||||
return (err >= 0);
|
||||
}
|
||||
|
||||
int nv_avc_get_rgb_frame(char* buffer, int size) {
|
||||
return (update_rgb_frame() && render_rgb_to_buffer(buffer, size));
|
||||
}
|
||||
|
||||
int nv_avc_get_input_padding_size(void) {
|
||||
return FF_INPUT_BUFFER_PADDING_SIZE;
|
||||
}
|
||||
|
||||
// packets must be decoded in order
|
||||
// indata must be inlen + FF_INPUT_BUFFER_PADDING_SIZE in length
|
||||
int nv_avc_decode(unsigned char* indata, int inlen) {
|
||||
int err = 0;
|
||||
int got_pic = 0;
|
||||
|
||||
pkt.data = indata;
|
||||
pkt.size = inlen;
|
||||
|
||||
while (pkt.size > 0) {
|
||||
got_pic = 0;
|
||||
err = avcodec_decode_video2(
|
||||
decoder_ctx,
|
||||
dec_frame,
|
||||
&got_pic,
|
||||
&pkt);
|
||||
if (err < 0) {
|
||||
//__android_log_write(ANDROID_LOG_ERROR, "NVAVCDEC",
|
||||
// "Decode failed");
|
||||
got_pic = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
pkt.size -= err;
|
||||
pkt.data += err;
|
||||
}
|
||||
|
||||
// Only copy the picture at the end of decoding the packet
|
||||
if (got_pic) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
// Only clone this frame if the last frame was taken.
|
||||
// This saves on extra copies for frames that don't get
|
||||
// rendered.
|
||||
if (yuv_frame == NULL) {
|
||||
// Clone a new frame
|
||||
yuv_frame = av_frame_clone(dec_frame);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// VideoDepacketizer.h
|
||||
// Limelight-iOS
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/18/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "VideoDecoder.h"
|
||||
|
||||
@interface VideoDepacketizer : NSOperation <NSStreamDelegate>
|
||||
@property uint8_t* byteBuffer;
|
||||
@property unsigned int offset;
|
||||
@property VideoDecoder* decoder;
|
||||
@property NSString* file;
|
||||
@property UIView* target;
|
||||
|
||||
- (id) initWithFile:(NSString*) file renderTarget:(UIView*)renderTarget;
|
||||
|
||||
@end
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// VideoDepacketizer.m
|
||||
// Limelight-iOS
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/18/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VideoDepacketizer.h"
|
||||
|
||||
@implementation VideoDepacketizer
|
||||
static int BUFFER_LENGTH = 131072;
|
||||
|
||||
- (id)initWithFile:(NSString *)file renderTarget:(UIView*)renderTarget
|
||||
{
|
||||
self = [super init];
|
||||
self.file = file;
|
||||
self.target = renderTarget;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
NSInputStream* inStream = [[NSInputStream alloc] initWithFileAtPath:self.file];
|
||||
self.byteBuffer = malloc(BUFFER_LENGTH);
|
||||
self.decoder = [[VideoDecoder alloc]init];
|
||||
|
||||
|
||||
[inStream open];
|
||||
while ([inStream streamStatus] != NSStreamStatusOpen) {
|
||||
NSLog(@"stream status: %d", [inStream streamStatus]);
|
||||
sleep(1);
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
unsigned int len = 0;
|
||||
|
||||
len = [inStream read:self.byteBuffer maxLength:BUFFER_LENGTH];
|
||||
if (len)
|
||||
{
|
||||
BOOL firstStart = false;
|
||||
for (int i = 0; i < len - 4; i++) {
|
||||
self.offset++;
|
||||
if (self.byteBuffer[i] == 0 && self.byteBuffer[i+1] == 0
|
||||
&& self.byteBuffer[i+2] == 0 && self.byteBuffer[i+3] == 1)
|
||||
{
|
||||
if (firstStart)
|
||||
{
|
||||
// decode the first i-1 bytes and render a frame
|
||||
//[self.decoder decode:self.byteBuffer length:i];
|
||||
[self.target performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:NULL waitUntilDone:FALSE];
|
||||
|
||||
// move offset back to beginning of start sequence
|
||||
[inStream setProperty:[[NSNumber alloc] initWithInt:self.offset-4] forKey:NSStreamFileCurrentOffsetKey];
|
||||
self.offset -= 1;
|
||||
|
||||
break;
|
||||
} else
|
||||
{
|
||||
firstStart = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"No Buffer! restarting file!");
|
||||
// move offset back to beginning of start sequence
|
||||
self.offset = 0;
|
||||
[inStream close];
|
||||
inStream = [[NSInputStream alloc] initWithFileAtPath:self.file];
|
||||
[inStream open];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// VideoRenderer.h
|
||||
// Limelight-iOS
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/19/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface VideoRenderer : NSOperation
|
||||
@property UIView* renderTarget;
|
||||
- (id) initWithTarget:(UIView*)target;
|
||||
+ (void) startRendering;
|
||||
+ (void) stopRendering;
|
||||
+ (BOOL) isRendering;
|
||||
@end
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// VideoRenderer.m
|
||||
// Limelight-iOS
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/19/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VideoRenderer.h"
|
||||
|
||||
@implementation VideoRenderer
|
||||
static bool render = false;
|
||||
|
||||
- (id)initWithTarget:(UIView *)target
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
self.renderTarget = target;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (render)
|
||||
{
|
||||
[self.renderTarget performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:NULL waitUntilDone:TRUE];
|
||||
usleep(10000);
|
||||
} else {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) startRendering
|
||||
{
|
||||
render = true;
|
||||
}
|
||||
|
||||
+ (void) stopRendering
|
||||
{
|
||||
render = false;
|
||||
}
|
||||
|
||||
+ (BOOL) isRendering
|
||||
{
|
||||
return render;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// VideoDecoderRenderer.h
|
||||
// Limelight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/18/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface VideoDecoderRenderer : NSOperation
|
||||
|
||||
- (id)initWithTarget:(UIView *)target;
|
||||
|
||||
@property UIView* renderTarget;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// VideoDecoderRenderer.m
|
||||
// Limelight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/18/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VideoDecoderRenderer.h"
|
||||
|
||||
@implementation VideoDecoderRenderer
|
||||
|
||||
- (id)initWithTarget:(UIView *)target
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
self.renderTarget = target;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)main
|
||||
{
|
||||
NSLog(@"Hi");
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user