בוא תראה את האפליקציה שלי ב TV של הסלון … | גיא הדרכה ופיתוח תוכנה

בוא תראה את האפליקציה שלי ב TV של הסלון …

אתם מכירים את זה שאתם רוצים להציג את האפליקציה שלכם למספר אנשים סביב שולחן (לא עגול) ולא התאפשר לכם לעשות את זה, רק משום שלא היתה לכולם ריאות טובה לכיוון המכשיר ??

אז אני חושב שיש לי פתרון עבורכם ואני כמובן אשמח לדעת איך זה עזר לכם…

אז איך זה עובד?

ישנן פונקציות API מתוך ה- SDK שכל עיקרן הוא יציאות הוידאו מהמכשיר. ישנה מחלקה בשם MPTVOutWindow שתציג עבורכם כל מה שיש בתוכה דרך יציאת הוידאו (כן כן, הכבל הזה שעולה קצת …). כמה מפתחים בעולם ישבו וגילו את אופן השימוש במחלקה הזו… והנה דוגמה פשוטה…
עבור הדוגמה ניצור קטגוריה ל- UIApplication שהיא למעשה הרחבה למחלקה של UIApplication. מחלקה זו תדאג להציג השתקפות של מה שאנחנו רואים במסך המכשיר דרך יציאת הוידאו. אין בעיה להשתמש במחלקה זו, משום שאנחנו לא מחוייבים לשינויים מיוחדים בקוד שכבר כתבנו באפליקציה.
כל שעלינו לעשות הוא פשוט לייבא את המחלקה לפרויקט שלנו ולקמפל … וזהו :)
זה באמת כזה פשוט, רק שעלינו לשים לב לכמה אזהרות חשובות:
1. אפליקציות שתעשנה שימוש בשיטות הללו – לא יורשו לעלות לחנות הפאליקציות הרשמית של אפל. אך מכיוון שהנכם מפתחים ואתם רק רוצים להציג את האפליקציה ע"ג ה TV לשימושכם הפרטי, זה נורמלי לחלוטין, כל עוד אתם לא שוכחים להעיף את הקוד המציג על המסך כולל ה-Framework.
2. השיטה אינה עובדת על העתקת פריטים שמוצגים ב – OpenGL !

כבל

אז כאמור מדובר בכבל הרגיל של אפל או כל חברה מורשית אחרת, שמאפשרת לצפות במדיה מהמכשיר ב TV . אתם מוזמנים לבקר בכל חנות ולקנות את הכבל …

לתכל'ס

שמרו את הקובץ הבא בשם: UIApplicationTVOut.m
והוסיפו אותו לפרויקט שלכם.
//
//  UIApplicationTVOut.m

#define kFPS 10
#define kUseBackgroundThread	YES

@interface UIApplication (tvout)
- (void) startTVOut;
- (void) stopTVOut;
- (void) updateTVOut;
@end

@interface MPTVOutWindow : UIWindow
- (id)initWithVideoView:(id)fp8;
@end

@interface MPVideoView : UIView
- (id)initWithFrame:(struct CGRect)fp8;
@end

@implementation MPVideoView (tvout)
- (void) addSubview: (UIView *) aView
{
    [super addSubview:aView];
}
@end

CGImageRef UIGetScreenImage();

@interface UIImage (tvout)
+ (UIImage *)imageWithScreenContents;
@end

@implementation UIImage (tvout)
+ (UIImage *)imageWithScreenContents
{
    CGImageRef cgScreen = UIGetScreenImage();
    if (cgScreen) {
        UIImage *result = [UIImage imageWithCGImage:cgScreen];
        CGImageRelease(cgScreen);
        return result;
    }
    return nil;
}
@end

UIWindow* deviceWindow;
MPTVOutWindow* tvoutWindow;
NSTimer *updateTimer;
UIImage *image;
UIImageView *mirrorView;
BOOL done;

@implementation UIApplication (tvout)

// if you uncomment this, you won't need to call startTVOut in your app.
// but the home button will no longer work! Other badness may follow!
//
// - (void)reportAppLaunchFinished;
//{
//	[self startTVOut];
//}

- (void) startTVOut;
{
	// you need to have a main window already open when you call start
	if (!tvoutWindow) {
		deviceWindow = [self keyWindow];

		MPVideoView *vidView = [[MPVideoView alloc] initWithFrame: CGRectZero];
		tvoutWindow = [[MPTVOutWindow alloc] initWithVideoView:vidView];
		[tvoutWindow makeKeyAndVisible];
		tvoutWindow.userInteractionEnabled = NO;

		mirrorView = [[UIImageView alloc] initWithFrame: [[UIScreen mainScreen] bounds]];

		if ([UIApplication sharedApplication].statusBarOrientation != UIInterfaceOrientationPortrait) {
			mirrorView.transform = CGAffineTransformRotate(mirrorView.transform, M_PI * 1.5);
		}
		mirrorView.center = vidView.center;
		[vidView addSubview: mirrorView];

		[deviceWindow makeKeyAndVisible];

		[NSThread detachNewThreadSelector:@selector(updateLoop) toTarget:self withObject:nil];
	}
}

- (void) stopTVOut;
{
	done = YES;
	if (updateTimer) {
		[updateTimer invalidate];
		[updateTimer release];
		updateTimer = nil;
	}
	if (tvoutWindow) {
		[tvoutWindow release];
		tvoutWindow = nil;
	}
}

- (void) updateTVOut;
{
	image = [UIImage imageWithScreenContents];
	mirrorView.image = image;
}

- (void)updateLoop;
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    done = NO;

    while ( ! done )
    {
		[self performSelectorOnMainThread:@selector(updateTVOut) withObject:nil waitUntilDone:NO];
        [NSThread sleepForTimeInterval: (1.0/kFPS) ];
    }
    [pool release];
}

@end

יש להוסיף את “MediaPlayer.framework” לסביבת הפיתוח, סביבה זו מכילה הפניות לאותן פונקציות להן נזדקק למימוש הרעיון.
הקוד שלכם צריך להכיל את הקריאה לפונקציה startTVOut בשיטה הבאה:
[[UIApplication] sharedApplication] startTVOut];

בכל פעם שאתם מאתחלים את החלון הראשי באפליקציה שלכם יש לקרוא ל startTVOut
באפליקצית הניסיון שלי, אני קורא ל startTVOut מתוך applicationDidFinishLaunching. דבר חשוב לזכור בעניין applicationDidFinishLaunching:שהיא נקראת עוד לפני שלולאת התצוגה מתחילה "runloop", לכן המסך הראשי טרם הוצג על מסך המכשיר. לשם כך, אני אקרא לפונקציה שניה לאחר מכן כמו בדוגמה הבאה:
- (void)applicationDidFinishLaunching:(UIApplication*)application {
// give the runloop a chance to start
[[UIApplication sharedApplication] performSelector: @selector(startTVOut)
withObject: nil afterDelay: .1];
}
זהו, זה כל מה שעליכם לעשות …
עבור האמיצים שבינכם, תוכלו לממש את זה כפונקציה אופציונלית באפליקציה עצמה…
הקוד כולל שיכתוב השיטה הפרטית : reportAppLaunchFinished. נראה שהשיטה הלא מתועדת הזו נקראת רק אחרי שהאפליקציה סיימה את הטעינה (Loading). ובכ"ז, למען התיעוד, אני לא ממש בטוח מה זה עושה, או מה ההשלכות השליליות שיש בשכתוב הפונקציה. כל שאני יכול לומר על הפוקציה הזו (מהיכרותי את ה SDK), שהיא משמשת רק כדי לנקות את התצוגה Default.png. אני הוצאתי "בהערה"  את הפוקציה עקב פעילותה המסתורית. אבל אם תחליטו לחשוף את הפונקציה, היא תעשה שימוש אוטומטי בתצוגה דרך יציאת ה TV. אין צורך בשינויים בקוד!
למעשה השימוש בקוד נותן לכם כל מה שרציתם ע"מ שתוכלו להציג את האפליקציה שלכם ע"ג ה TV.

איך זה נעשה?

הפוקציה startTVOut יוצרת את MPTVoutWindow, מוסיפה את תתי האלמנטים, מציגה את המסך באורינטציה המתאימה וממרכזת הכל ע"מ שהמצג יתאים לתצוגה ב TV.
ישנו תהליך (Thread) שפועל ברקע ומעתיק את מפות הסיביות של המסך הראשי לתוך MPTVOutWindow.
ישנה אפשרות לשנות את מספר הפריימים לשניה (10 בדוגמה אך ניתן לשנות ל 12 זה עובד לי מצויין).

מה עם הצגת אפליקציות נוספות שאינן שלי ?

תראו, מכיוון שמדובר בקוד שפועל בתוך האפליקציה שלכם, לא תוכלו ליישם את השיטה עבורן. ולכן השיטה תפסיק לעבוד כאשר תצאו מהאפליקציה שלכם.
לכל מקרה, ע"י שימוש בפוקציות פרטיות אחרות (קצרה היריעה מלפרט)… ניתן לשלוט על פעילותן הויזואלית של אפליקציות אחרות שאינן שלכם. בשלב זה הצלחתי ליישם את זה על דפדפן האיפון ה- Safari. ע"י שימוש בפונקציה –  openURL:asPanel: method .
אני בטוח שתוכלו למצא שיטה איך עושים את זה בצורה הטובה ביותר עם הפוקציות הפרטיות מתוך סביבת הפיתוח של המדיה.

לסיכום

חברים, אני יותר מאשמח לקבל מכם מאמרים שאתם כתבתם ולפרסם אותם כאן בשמכם, זה יתרום לכם ולכולנו …
ובכלל, אם יש לכם עוד רעיונות למאמרים מעניינים שתרצו שאכתוב, אני אשמח לשתף…
12 מרץ, 2010 תחת קטגוריה מאמרים - המשך קריאה

2 Responses to “בוא תראה את האפליקציה שלי ב TV של הסלון …”

  1. מאת Valentina:

    Wow! Great to find a post with such a clear megssae!

  2. מאת אלי שלומוב:

    חחח, אני ראשון :)
    אחי, הטקסט יצא לך מהגבול!
    ומאמר פשוט מעולה, בדיוק חיפשתי פתרון למצגות.

    אבל שאלה: למה זה עובד לי לאט כ"כ?

כתיבת תגובה

  • תגים