Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"The AMP Auto Skip command now utilizes Preroll if the preroll value is greater than or equal to the K2’s specified preroll setting (10 frames). The Auto Skip Option 1 was removed from the spec because it was not supported. A new option was added to this command, option 4. You can now schedule an Auto Skip against timecode, just as you would do with Play, Stop, or Record. The AMP Specification was update to reflect this new command option.","buildsWithFix":"K2 3.2.58.858","assocCRs":"90348","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"buildsWithFix":"K2 3.2.58.858","date":"2008-04-02","description":"Four AMP Channelless commands were not working correctly under certain conditions. The Set Mark In, Set Mark Out, ID Start Time, and ID Duration Request commands all were not performing correctly because a channelless connection does not have a video format. These commands were changed to extract the video format from the clips they are performing operations on. ","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x","assocCRs":"89952"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-29","description":"Resolves issue with a checkin made on 1/4/08 in ParseClipNames(). If the same clip was already cued when a cue clip command came in (In Preset or Preview In Preset), some of the status bits were not getting set correctly.","assocCRs":"86634","checkinTimeBranch":"1/29/2008 6:25 PM - K2_maint_4\n2/1/2008 1:19 PM - Main\n","buildsWithFix":"K2 3.2.58.831"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-02-29","description":"Only update channel info for active context. AMP was being told that the current clip was in fact the clip that was cued on the preview context.","assocCRs":"88538","checkinTimeBranch":"2/29/2008 9:50 AM - K2_maint_4\n2/29/2008 9:52 AM - Main\n2/29/2008 9:51 AM - K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"The AMP Device Type Request now sets bit 0 high if in PAL mode.","assocCRs":"83028","buildsWithFix":"K2 3.2.58.858","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"description":"The Drop Frame status was being reported in PAL mode.","date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90350","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-13","description":"A change was checked into K2 code to fix an issue identified with the AMP Erase Segment command. The command would sometimes not clean up unused media because the new marks were not being flushed to the database quick enough. This was fixed by adding a call to manually flush the changes to the DB. ","checkinTimeBranch":"12/13/2007 4:16 PM - K2_maint_4\n12/13/2007 4:16 PM - Main\n12/13/2007 4:16 PM - k2_3.x","buildsWithFix":"K2 3.2.58.817"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"This fix changes the behavior in AMP PreviewInPreset command if it is given an invalid clip name. Previously: stops currently playing (In Preset) clip and cues to Mark In. Now: currently playing clip continues playing, clip not found message logged, IDNotFound status bit is set.","buildsWithFix":"K2 3.2.58.858","assocCRs":"78507","checkinTimeBranch":"4/2/2008 10:09 AM / K2_maint_4\r\n4/2/2008 9:01 AM / Main\r\n4/2/2008 9:00 AM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-04","description":"A change was checked into K2 code to fix an issue identified with the In Preset commands. You can now cue clips with full paths from various different folders, all at once, on the same timeline. The ID Loaded Request now also returns the currently playing clip correctly, after multiple clips from different directories had been cued at once, on one timeline. ","assocCRs":"86634","checkinTimeBranch":"1/4/2008 3:53 PM - K2_maint_4\n2/1/2008 1:19 PM - Main\n","buildsWithFix":"K2 3.2.58.820"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"description":"Properly handle clip object lists when Preview In Preset command is issued with no clip loaded on the In Preset timeline.","date":"2008-02-29","assocCRs":"88557","checkinTimeBranch":"2/29/2008 9:57 AM - K2_maint_4\n2/29/2008 9:58 AM - Main\n3/3/2008 12:12:21 PM - K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"The AMP Play command was not using preroll if the player was stopped at the end of the In Preset clip. It now uses the preroll setting if the preroll is greater than or equal to the specified preroll setting (10 frames on K2).","buildsWithFix":"K2 3.2.58.858","assocCRs":"90352","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90347","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x","description":"The AMP ability to retrieve the clip lists from the Preview Preset and Preset commands using option 2 of the commands was broken. It would not pass the clip lists from the Preview Timeline to the Preset timeline when the clips in the Preview Timeline transitioned to playing (when they were activated)."}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90351","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x","description":"The AMP Preview Out Preset option 2, clip list retrieval, was not subtracting the Mark In from the Mark Out when returning the Out Preset times when Timer timecode mode was set."}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90349","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main\r\n4/2/2008 2:52 PM / K2_3.x","description":"The AMP Preview Out Preset status bit was being set low in the In Preset command Option 2, command was received. This is simply the retrieval of the clip list and should not have modified any status bits."}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"description":"It was found that the Preview In and Preview Out status bits were being set high prematurely. This fix addresses that and the bits are now set only when the decoder finishes cueing the clip.","date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"89866","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x\r\n"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-12","description":"A change was checked into K2 code to fix an issue identified with the recently added AMP Replace Edit feature. When connected to a channel-less AMP connection, the Replace Edit command was not properly acquiring clip properties for the virtual clip ‘<BLACK>’. ","assocCRs":"86355","checkinTimeBranch":"12/12/2007 10:44 AM - K2_maint_4\n12/12/2007 10:44 AM - Main\n12/12/2007 10:44 AM - k2_3.x","buildsWithFix":"K2 3.2.58.814"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-02-29","description":"Changing shuttle function to CommitChange() as type NONE. Lack of the choice 'none' always forces a recue of the preview context when attempting to change the shuttle speed on the preview context. Shuttles near the end of a context can mean that the next context will fail to cue in time.","assocCRs":"85999","checkinTimeBranch":"2/29/2008 9:57 AM - K2_maint_4\n2/29/2008 9:58 AM - Main\n3/3/2008 12:12:21 PM - K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-12","description":"Changes were checked into K2 code to fix some issues identified with the recently added AMP End Mode feature. When in Variable Play mode or Fast Forward mode, if an End Mode was set, the playback would not stop at the end of the play timeline like it should have. Also, when playing at a speed other than 1.0 (play speed), if the End Mode was switched back to Default, the play speed for the preview timeline was always being set to 1.0. It is now set to the speed that the channel is currently playing at. Another fix found is when the Jog command was issued; it also could be applied to the preview context. ","checkinTimeBranch":"12/12/2007 10:44 AM - K2_maint_4\n12/12/2007 10:44 AM - Main, k2_3.x\n12/12/2007 10:44 AM - k2_3.x\n","assocCRs":"86477","buildsWithFix":"K2 3.2.58.814"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"Time of Day timecode mode support was added. It is now supported by the Timecode Mode Preset (41.36) command. Time of Day is set with Send Data equal to 0x03. A time of day status bit was also implemented to reflect the new timecode mode. The status bit is Byte D, bit 0. The AMP specification was also updated to reflect this.","buildsWithFix":"K2 3.2.58.858","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main\r\n4/2/2008 2:52 PM / K2_3.x","assocCRs":"90346"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-24","description":"Issue fixed regarding caching clips in a directory with a UTF-8 directory name.","assocCRs":"86944","checkinTimeBranch":"1/24/2008 11:40 AM - K2_maint_4\n2/1/2008 1:19 PM - Main\n","buildsWithFix":"K2 3.2.58.831"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-02-29","description":"User Data is deleted upon trimming a clip. This is ok if the data is associated with a field number in the clip that has been trimmed, but user data that is associated with the entire clip as a whole was also being deleted.","assocCRs":"87856","checkinTimeBranch":"2/29/2008 9:44 AM / K2_maint_4\n2/29/2008 9:52 AM / Main\n2/29/2008 9:46 AM / K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-06","description":"A change was checked into K2 code to fix a cueing issue related to clips that were recorded past the midnight boundary. Passing the midnight boundary means a clip’s timecode will increase to 23:59:59:29 (NTSC Example) then be followed by the timecode 00:00:00:00 in the next frame. If you tried to cue a clip to a timecode greater than or equal to 00:00:00:00 inside that clip, the cue operation would fail. This issue only occurred when the timecode mode was set to Timer Mode. Cueing to absolute timecode did not experience this issue. The issue currently still exists in the BVW protocol for both Timer and Absolute timecode modes, and will be addressed in another future build.","buildsWithFix":"K2 3.2.58.808","checkinTimeBranch":"12/6/2007 4:15 PM - K2_maint_4\n12/6/2007 4:15 PM - main, k2_3.x\n12/6/2007 4:15 PM - k2_3.x","assocCRs":"85945"}</data>
!K2 ~AppServer Developer's Guide © Grass Valley 2007
The K2 ~AppServer Developer's Guide was written using Jeremy Ruston's excellent [[TiddlyWiki|http://www.tiddlywiki.com/]] (version: <<version>>). ~TiddlyWiki is used under a [[BSD open source license|http://www.tiddlywiki.com/#OpenSourceLicense]].
This guide also uses plugins from [[abego Software GmbH|http://tiddlywiki.abego-software.de/#Plugins]]. The plugins are used under a [[BSD open source license|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D%20About]].
The guide also references [[Oleg Shilo's CS-Script|http://www.members.optusnet.com.au/~olegshilo/index.html]] program.
''Copyright''
Copyright © 2007 Grass Valley, Inc. All rights reserved. Printed in the United States of America. Portions of software © 2000 – 2007, Microsoft Corporation. All rights reserved. This document may not be copied in whole or in part, or otherwise reproduced except as specifically permitted under U.S. copyright law, without the prior written consent of Grass Valley, Inc., P.O. Box 59900, Nevada City, California 95959-7900. This product may be covered by one or more U.S. and foreign patents.
''Disclaimer''
Product options and specifications subject to change without notice. The information in this manual is furnished for informational use only, is subject to change without notice, and should not be construed as a commitment by Grass Valley, Inc. Grass Valley, Inc. assumes no responsibility or liability for any errors or inaccuracies that may appear in this publication.
''U.S. Government Restricted Rights Legend''
Use, duplication, or disclosure by the United States Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.277-7013 or in subparagraph c(1) and (2) of the Commercial Computer Software Restricted Rights clause at FAR 52.227-19, as applicable. Manufacturer is Grass Valley, Inc., P.O. Box 59900, Nevada City, California 95959-7900 U.S.A.
''Trademarks and Logos''
Grass Valley, K2, Aurora, Turbo, ~M-Series, Profile, Profile XP, ~NewsBrowse, ~NewsEdit, ~NewsQ, ~NewsShare, ~NewsQ Pro, and Media Manager are either registered trademarks or trademarks of Grass Valley, Inc. in the United States and/or other countries. Grass Valley, Inc. products are covered by U.S. and foreign patents, issued and pending. Additional information regarding Grass Valley, Inc. trademarks and other proprietary rights may be found at www.thomsongrassvalley.com.
Other trademarks and logos used in this document are either registered trademarks or trademarks of the manufacturers or vendors of the associated products, such as Microsoft® Windows® operating system, Windows Media® player, Internet Explorer® internet browser, and SQL Server™. ~QuickTime and the ~QuickTime logo are trademarks or registered trademarks of Apple Computer, Inc., used under license therefrom.
''Suites and Security:''
When a client requests an ~AppServer, it passes in a unique suite name and a user name. The concept is similar to someone using an editing suite. While that person is in the suite, they have access to all of their own resources. Similarly, each ~AppServer for a user and suite has its own resources. This is also what tells ~AppService whether-or-not to create a new ~AppServer. If two client applications request an ~AppServer with the same suite name and user name, then both applications will get back the same ~AppServer object. See [[Suspending and Closing AppServers]] for an explanation of when an existing ~AppServer would be returned.
''Channel stealing:''
Channels on the K2 are shared resources. If a client is using a channel and another client requests to use that same channel, the channel will be stolen away from the first client and given to the second client. If it is important to prohibit this, the applicatoin should first check to see if the channel is owned by another application first. See the [[Examples]] section for channel owner example code.
This information is taken fom the “K2 Media Client User Guide”, Chapter 7, Working with assets – Renaming an asset:
Asset names can contain up to 32 characters, including spaces. Spaces count as two characters.
The following characters are not allowed:
\ (backward slash)
/ (forward slash)
: (colon)
* (asterisk)
? (question mark)
< (less than)
> (greater than)
¦ (pipe)
" (double quote)
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|assetType|BSTR|System.String|String indicates the asset type. Values include Clip, Program, and List||
|bin|BSTR|System.String|Name of the bin that contains the asset.|e|
|construction|BSTR|System.String|Indicates the asset’s construction status. Values include: ~CodecConstruction, ~CopyConstruction, ~StreamConstruction, ~RestoreConstruction, and ~DoneConstruction.||
|created|DATE|System.~DateTime|Date created.||
|dropFrame|~VARIANT_BOOL|System.Boolean|~VARIANT_TRUE if the asset’s timecode media indicates drop-frame.||
|eventCount|long|System.Int32|Number of events in a List asset.|e|
|fileLength|long|System.Int32|Difference between maxMarkOut and minMarkIn properties.|e|
|fileLengthStr|BSTR|System.String|Timecode string representation of the difference between maxMarkOut and minMarkIn properties.|e|
|firstTimecodeMask|long|System.Int32|Cached timecode value from the start of the first timecode media file. Timecode is represented as a 32 bit mask.||
|id|BSTR|System.String|32 character unique identifier (UUID)||
|isinuse|~VARIANT_BOOL|System.Boolean|~VARIANT_TRUE if the movie is locked, has another writable instance open, is under construction, or is loaded on the system processing the property query.||
|lastTimecodeMask|long|System.Int32|Cached timecode value from the end of the last timecode media file. Timecode is represented as a 32 bit mask.||
|lengthStr|BSTR|System.String|String representing the ~MaxLength value converted to timecode. A ‘;’ character before the frame value indicates dropframe.||
|locked|~VARIANT_BOOL|System.Boolean|~VARIANT_TRUE if the asset is locked. ||
|markIn|long|System.Int32|Asset mark in value. Does not necessarily reflect media marks.||
|markInStr|BSTR|System.String|String representation of the timecode from the start of the first timecode media file on the first timecode track. A ‘;’ or ‘,’ character before the frame value indicates dropframe.||
|markOut|long|System.Int32|Asset mark out value. Does not necessarily reflect media marks.||
|markOutStr|BSTR|System.String|String representation of the timecode from the end of the last timecode media file on the first timecode track. Note that this value is one frame greater than the last playable timecode frame. A ‘;’ or ‘,’ character before the frame value indicates dropframe.||
|maxLength|long|System.Int32|Number of fields in longest track (the sum of all media durations).||
|maxMarkOut|long|System.Int32|This is the largest value the mark out can be set to and still show media on all tracks.|e|
|maxMarkOutStr|BSTR|System.String|This is a string representation of the smallest timecode value the mark in can be set to and still show media on all tracks. The timecode value is mathematically calculated using the firstTimecode property.|e|
|minMarkIn|long|System.Int32|This is the smallest value the mark in can be set to and still show media on all tracks.|e|
|minMarkInStr|BSTR|System.String|This is a string representation of the smallest timecode value the mark in can be set to and still show media on all tracks. The timecode value is mathematically calculated using the firstTimecode property.|e|
|modified|DATE|System.~DateTime|Date last modified.||
|movieSize|long|System.~UInt32|The sum of media file sizes and user data in megabytes (MB). If the movie only references a portion of a file, then only a percent of the file size value is used.|e, x|
|name|BSTR|System.String|Asset name||
|numA|long|System.Int32|Number of audio tracks.||
|numT|long|System.Int32|Number of timecode tracks.||
|numV|long|System.Int32|Number of video tracks.||
|readOnly|~VARIANT_BOOL|System.Boolean|~VARIANT_TRUE if the asset is locked or is a read-only instance (which can happen if some other application has the asset already open for writing). Applications can only lock and unlock assets. ~ReadOnly is merely reflects the current state of the asset.||
|repeat|~VARIANT_BOOL|System.Boolean|~VARIANT_TRUE if this is a List asset type with the “repeat” property.||
|sectionCount|long|System.Int32|Number of sections in a List asset.|e|
|starttimecode|long|System.Int32|Arbitrary starting timecode for a List asset. Timecode is represented as a 32 bit mask.|e|
|starttimecodestr|BSTR|System.String|Arbitrary starting timecode for a List asset. Blank if a starting timecode was never defined.|e|
|summary|BSTR|System.String|English-based summary of the asset structure. This string is used by utility programs like ~ViewMovie.|e, x|
|thumbnail|BYTE SAFEARRAY||Jpeg thumbnail.|e|
|thumbnailMark|long|System.Int32|Thumbnail mark value. This value is relative to the asset’s mark in value. For example, if the thumbnail is taken 32 fields into a clip whose mark in value is 100, then the thumbnail mark value would be 132.|e|
|underConstruction|~VARIANT_BOOL~|System.Boolean|~VARIANT_TRUE if the asset is under any type of construction.||
|uri|BSTR|System.String|Asset identifier in URI format. The URI string takes the following form: {{{edl/cmf//<machine>/<volume>/<bin>/<name>}}}|e|
|version|long|System.Int32|Version of this property list. Currently set to 1.||
|videoCompressionType|BSTR|System.String|Compression type of the first video track. Values include Mpeg, ~DV25, ~DV50, Jpeg, and ~MpegHiDef.||
|videoFormat|long|System.Int32|Asset video format. Enumerated values include: {{{Format525_60Hz_2To1 = 0, Format625_50Hz_2To1, Format720_59_94Hz_1To1, Format720_60Hz_1To1, Format1080_23_98Hz_1To1, Format1080_24Hz_1To1, Format1080_25Hz_1To1, Format1080_29_97Hz_1To1, Format1080_30Hz_1To1, Format1080_25Hz_2To1, Format1080_29_97Hz_2To1, Format1080_30Hz_2To1, Format1035_30Hz_2To1, Format1035_29_97Hz_2To1, VideoFormat_NA, Unknown.}}}|
|videoFormatStr|BSTR|System.String|Asset video format. Values include: {{{Format525_60Hz_2To1, Format625_50Hz_2To1, Format720_59_94Hz_1To1, Format720_60Hz_1To1, Format1080_23_98Hz_1To1, Format1080_24Hz_1To1, Format1080_25Hz_1To1, Format1080_29_97Hz_1To1, Format1080_30Hz_1To1, Format1080_25Hz_2To1, Format1080_29_97Hz_2To1, Format1080_30Hz_2To1, Format1035_30Hz_2To1, Format1035_29_97Hz_2To1, VideoFormat_NA, Unknown.}}}||
|volume|BSTR|System.String|Name of the disk volume associated with the asset’s bin.|e|
|xml|BSTR|System.String|XML representation of the asset.|e, x|
Notes:
e – Best performance with one of the Editor interfaces (~ISimpleEditor, ~IEventEditor). Properties with this note can still be obtained by ~IMediaMgr::~GetProperty, but extra database transactions have to be made that makes these properties more expensive.
x – Expensive. This properties should only be obtained occasionally and as a result of a user request, rather than on a timer.
To Get and Set user data for an asset use the ~MediaMgr functions show in the examples below.
{{{
[C#]
// get a MediaMgr object
using ( IMediaMgr mediaMgr = connection.AppServer.CreateMediaMgr(appName) )
{
int cookie = -1;
try
{
// define an asset that we want to get & set userdata
string clip = "edl/cmf//local/V:/default/Clip";
// add userdate - string, integer, float, boolean, date
mediaMgr.AddMetadata (clip, "SomeName", "SomeValue");
mediaMgr.AddMetadata (clip, "SomeInteger", 5);
mediaMgr.AddMetadata (clip, "SomeFloat", (float) 3.14);
mediaMgr.AddMetadata (clip, "SomeBoolean", true);
mediaMgr.AddMetadata (clip, "SomeDate", DateTime.Now);
// select the user data for a clip
int count = 0;
cookie = mediaMgr.SelectMetadata (clip, ref count);
Console.WriteLine("count = " + count);
if ( count > 0 )
{
// get all user data tags
string tags;
mediaMgr.GetAllMetadataTags(out count, out tags);
// print tags - see sample GetAllMetadataTags below class
Console.WriteLine(tags);
Console.WriteLine("-----------");
// using GetXmlResults get all userdata for a clip in XML format
int maxCount = 512; // loop thru all results in blocks of 512 lines
// keep in 256 - 512 range for best performance
int i = 0; // used to indicate where to start
string xmlData = "";
count = maxCount;
// loop thru all results until done
while (count == maxCount)
{
// get a block of user data and print it out
mediaMgr.GetXmlResults(cookie, i * maxCount, maxCount,
ref count, out xmlData);
Console.WriteLine(xmlData);
// see sample GetXmlResults below class
i++;
}
}
// remove metadata - get ExtensionID from XML data from GetXmlResults call.
// hardcoded it here for simple example.
string extensionID = "443c567a96394a679ccee6ecf17648b8";
mediaMgr.RemoveMetadata (clip, extensionID);
}
catch (COMException ce)
{
Console.WriteLine("ERROR: caught COM Exception 0x{0:X}.", ce.ErrorCode);
Console.WriteLine("Probably couldn't find metadata with that ExtensionID");
}
finally
{
if (cookie > -1)
mediaMgr.CloseResults(cookie);
}
}
}}}
{{{
[C++]
// get a MediaMgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(m_bstrAppName, &spMediaMgr);
// define an asset that we want to get & set userdata
_bstr_t bstrClip("edl/cmf//local/V:/default/Clip");
// add userdata - string, integer, float, boolean
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeName"), _variant_t("SomeValue"));
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeInteger"), _variant_t(5));
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeFloat"), _variant_t((float) 3.14));
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeBoolean"), _variant_t(TRUE));
// add userdata - date
COleDateTime oleDate(2008,3,17,12,15,0);
_variant_t varDate;
varDate.date = oleDate;
varDate.vt = VT_DATE;
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeDate"), varDate);
// select the user data for a clip
long count = 0;
long cookie;
spMediaMgr->SelectMetadata(bstrClip, &count, &cookie);
if ( count > 0 )
{
// get all user data tags
BSTR bstrTags;
spMediaMgr->GetAllMetadataTags(&count, &bstrTags);
CString sTags = bstrTags;
printf("%s\n", sTags);
printf("-----------\n");
// using GetXmlResults get all userdata for a clip in XML format
int maxCount = 512; // loop thru all results in blocks of 512 lines
// keep in 256 - 512 range for performance
int i = 0; // used to indicate where to start
BSTR bstrXmlData;
count = maxCount;
// loop thru all results until done
while (count == maxCount)
{
// get a block of user data and print it out
spMediaMgr->GetXmlResults(cookie, i * maxCount, maxCount,
&count, &bstrXmlData);
CString sXmlData = bstrXmlData;
printf("%s\n", sXmlData);
i++;
}
// remove metadata - get ExtensionID from XML data from GetXmlResults call.
// hardcoded here for simple example.
_bstr_t bstrExtensionID = _bstr_t("443c567a96394a679ccee6ecf17648b8");
spMediaMgr->RemoveMetadata (bstrClip, bstrExtensionID);
}
}}}
The ~MediaMgr function ''~GetAllMetadataTags()'' returns XML data that looks like this (for the above examples). See the "ns:Data" section towards the bottom for the data:
{{{
<XML>
<Header version="1"/>
<Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
<AttributeType name="metadataName" dt:type="string"/>
<AttributeType name="metadataType" dt:type="string"/>
<ElementType name="MetadataTag">
<attribute type="metadataName"/>
<attribute type="metadataType"/>
</ElementType>
<ElementType name="Data">
<element type="MetadataTag"/>
</ElementType>
</Schema>
<ns:Data xmlns:ns="x-schema:#Schema1">
<ns:MetadataTag metadataName="SomeBoolean" metadataType="boolean"/>
<ns:MetadataTag metadataName="SomeDate" metadataType="dateTime"/>
<ns:MetadataTag metadataName="SomeFloat" metadataType="r4"/>
<ns:MetadataTag metadataName="SomeInteger" metadataType="i4"/>
<ns:MetadataTag metadataName="SomeName" metadataType="string"/>
</ns:Data>
</XML>
}}}
The ~MediaMgr functions ''~SelectMetadata()''and ''~GetXmlResults()'' returns XML data that looks like this (for the above examples). See the "ns:Data" section towards the bottom for the data:
{{{
<Select Query="" StartIndex="">
<Header version="1" selectionNamespace="x-schema:#Schema1"/>
<Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
<AttributeType name="ExtensionId" dt:type="string"/>
<AttributeType name="MovieId" dt:type="string"/>
<AttributeType name="LocationId" dt:type="string"/>
<AttributeType name="Position" dt:type="i4"/>
<AttributeType name="DataClass" dt:type="string"/>
<AttributeType name="DataCreator" dt:type="string"/>
<AttributeType name="DataName" dt:type="string"/>
<AttributeType name="DataVersion" dt:type="string"/>
<AttributeType name="DataLength" dt:type="i4"/>
<AttributeType name="DataType" dt:type="enumeration" dt:values="bin.base64 string ui1 ui2 ui4 i1 i2 i4 r4 r8 boolean dateTime Unknown"/>
<AttributeType name="Data_bin.base64" dt:type="bin.base64"/>
<AttributeType name="Data_boolean" dt:type="boolean"/>
<AttributeType name="Data_dateTime" dt:type="dateTime"/>
<AttributeType name="Data_i1" dt:type="i1"/>
<AttributeType name="Data_i2" dt:type="i2"/>
<AttributeType name="Data_i4" dt:type="i4"/>
<AttributeType name="Data_ui1" dt:type="ui1"/>
<AttributeType name="Data_ui2" dt:type="ui2"/>
<AttributeType name="Data_ui4" dt:type="ui4"/>
<AttributeType name="Data_r4" dt:type="r4"/>
<AttributeType name="Data_r8" dt:type="r8"/>
<AttributeType name="Data_string" dt:type="string"/>
<ElementType name="Extension">
<attribute type="ExtensionId"/>
<attribute type="MovieId"/>
<attribute type="LocationId"/>
<attribute type="Position"/>
<attribute type="DataCreator"/>
<attribute type="DataClass"/>
<attribute type="DataName"/>
<attribute type="DataVersion"/>
<attribute type="DataType"/>
<attribute type="DataLength"/>
<attribute type="Data_bin.base64"/>
<attribute type="Data_boolean"/>
<attribute type="Data_dateTime"/>
<attribute type="Data_i1"/>
<attribute type="Data_i2"/>
<attribute type="Data_i4"/>
<attribute type="Data_ui1"/>
<attribute type="Data_ui2"/>
<attribute type="Data_ui4"/>
<attribute type="Data_r4"/>
<attribute type="Data_r8"/>
<attribute type="Data_string"/>
</ElementType>
<ElementType name="Data">
<element type="Extension"/>
</ElementType>
</Schema>
<ns:Data xmlns:ns="x-schema:#Schema1">
<ns:Extension ExtensionId="443c567a96394a679ccee6ecf17648b8" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeBoolean" DataVersion="1" DataType="boolean" DataLength="2" Data_boolean="1"/>
<ns:Extension ExtensionId="4a3373ef41684e2685790f400d1021a0" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeInteger" DataVersion="1" DataType="i4" DataLength="4" Data_i4="5"/>
<ns:Extension ExtensionId="96f61e9c534c49e4b3369169ec9116e8" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeFloat" DataVersion="1" DataType="r4" DataLength="4" Data_r4="3.14"/>
<ns:Extension ExtensionId="9c3d55b81c4e4760a40411f60242be60" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeName" DataVersion="1" DataType="string" DataLength="18" Data_string="SomeValue"/>
<ns:Extension ExtensionId="b8ec96ab0a974393b5e9c6a1793fde78" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeDate" DataVersion="1" DataType="dateTime" DataLength="8" Data_dateTime="2008-03-10T14:43:54.000"/>
</ns:Data>
</Select>
}}}
The commands listed here are either implemented or stubbed (returning only a command acknowledgment) as indicated. Each command is identified by its command code and name.
Information listed here is intended to specify the minimum requirements for each command. Unless otherwise specified, the server implements all the capabilities for each command as specified in the Sony documentation. For more information, refer to the second edition or later of the //Sony Videocassette Recorder Bvw Series Protocol Manual// (part number 9-977-544-11).
''Command list:''
The following table lists the BVW protocol commands. The server returns an ACK response to each command, unless otherwise indicated.
|''Command''|''Name''|''Return''|
|0X.11|Device Type Request|The controller can distinguish Profile from ~BVW-75: If 00.11 is sent, returns 20.25 or 21.25 (for 525- or 625-line opera¬tion respectively). If 01.11.00 is sent, returns D8.00 (disk recorder). This allows the controller to decide whether to cue-up by a combi¬nation of ~FastForward/Rewind and ~CueUpWithData (faster with ~BVW-75) or ~CueUpWithData (faster with Profile). |
|20.00|Stop||
|20.01|Play||
|20.02|Record||
|20.04|Standby Off|Stubbed. Acknowledges command only.|
|20.05|Standby On|Stubbed. Acknowledges command only.|
|20.0F|Eject||
|20.10|Fast Forward||
|2X.11|Jog Forward||
|2X.12|Variable Forward||
|2X.13|Shuttle Forward||
|20.20|Rewind||
|2X.21|Jog Reverse||
|2X.22|Variable Reverse||
|2X.23|Shuttle Reverse||
|20.30|Preroll||
|24.31|Cue Up With Data|Matches behavior of ~BVW-75 as follows: After start-up, or after the displayed timecode is changed from the "Options/Select Timecode ..."dialog box, ~CueUpWithData cues up using the following time codes: VITC if VITC recorder is displayed LTC if LTC recorder is displayed Timer1 otherwise After ~TimerModeSelect (41.36) has been sent, ~CueUpWithData cues up using the following time codes: VITC if 41.36.00 is sent and VITC is displayed LTC if 41.36.00 is sent and anything else is displayed Timer1 if 41.36.01 is sent|
|21.38|Program Play +||
|21.39|Program Play ¬||
|20.52|Tension Release|Stubbed. Acknowledges command only.|
|20.60|Full EE Off|Panel indicator (PB/EE) changes to match.|
|20.61|Full EE On|Panel indicator changes to EE only. This causes timecode, video, and audio to go to EE in the BVW (insert-edit) controller, and video and audio to go to EE in the BVW controller.|
|20.63|Selected EE On||
|20.64|Edit Off||
|20.65|Edit On|
|44.00|Timer-1 Preset||
|44.04|Time Code Preset||
|44.05|User Bit Reset||
|44.08|Timer-1 Reset||
|40.10|In Entry||
|40.11|Out Entry||
|44.14|In Preset||
|44.15|Out Preset||
|40.20|In Reset||
|40.21|Out Reset||
|40.24|In Recall||
|40.25|Out Recall||
|4X.30|Edit Preset||
|44.31|Preroll Time Preset||
|41.34|Head Select|Stubbed. Acknowledges command only.|
|41.36|Timer Mode Select||
|40.40|Auto Mode Off|Stubbed. Acknowledges command only.|
|40.41|Auto Mode On|Stubbed. Acknowledges command only.|
|61.0C|Current Time Sense||
|60.10|In Data Sense||
|60.11|Out Data Sense|
|61.20|Status Sense|
|6X.30|Edit Preset Sense|
|60.31|Preroll Time Sense|
|60.36|Timer Mode Sense|
|A0:01|Enable Clip Loading|
|A0:02|Disable Clip Loading|
|A0:10|Clip Length Request|Clip length response in the form 00:00:00:00.|
|A8:12|Set Clip Length|
|A0:20|Find First Clip Request|Clip name.|
|A0:21|Find Next Clip Request|Clip name.|
|AX:30|Load Clip||
|AX:40|New Clip||
|AX:50|Delete Clip||
The code below shows how to create a basic connection to a K2's ~AppService.
{{{
[C#]
// create an AppServerMgr proxy object
AppServerMgrProxy appServerMgrProxy = new AppServerMgrProxy();
// set the host name of the K2 server
string host = "K2machinename";
appServerMgrProxy.SetHost(host);
// set the user credentials
string username = "username";
string password = "password";
string domain = "domain";
appServerMgrProxy.SetUserCredentials(username, password, domain, false);
// connect to the AppServerMgr
if ( !appServerMgrProxy.Connect() )
Console.WriteLine("ERROR: Could not connect to AppService on " + host + ".");
else
Console.WriteLine("Successfully connected to AppService.");
// ... do some work ...
// disconnect
appServerMgrProxy.Disconnect();
}}}
{{{
[C++]
// Initialize the COM library
CoInitialize(NULL);
// create an AppServerMgr proxy object
IAppServerMgrProxyPtr spAppServerMgrProxy (__uuidof(AppServerMgrProxy));
// set the host name of the K2 server
_bstr_t bstrHost("K2machinename");
hr = spAppServerMgrProxy->SetHost(bstrHost);
// set the user credentials
_bstr_t bstrUsername("username");
_bstr_t bstrPassword("password");
_bstr_t bstrDomain("domain");
VARIANT_BOOL encrypted(FALSE);
hr = spAppServerMgrProxy->SetUserCredentials( bstrUsername, bstrPassword,
bstrDomain, encrypted);
// connect to the AppServerMgr
VARIANT_BOOL retVal;
hr = spAppServerMgrProxy->Connect(&retVal);
if ( !retVal )
printf("ERROR - couldn't connect to the AppServerMgr\n");
// ... do some work ...
// disconnect
spAppServerMgrProxy->Disconnect(&retVal);
}}}
Go to [[CS-Script - The C# Script Engine|http://www.members.optusnet.com.au/~olegshilo/]] website.
''~CS-Script'' (pronounced C sharp script) is a CLR (Common Language Runtime) based scripting system which uses ~ECMA-compliant C# as a programming language. It combines the power and richness of C# with the flexibility of a scripting system.
The C# samples below take advantage of ''~CS-Script'' by providing K2 code that will run from a single C# file. For instance, you could open a DOS prompt and run a script as easily as this:
{{{
C:\> cscs.exe PlayClip.cs
}}}
To use these samples you will first need to setup your machine:
1. If you are writing code on a PC rather than the K2, [[install the Control Point software|http://www.grassvalley.com/downloads/servers/k2_3.2.21.746.html]] on your PC.
2. Next install ''~CS-Script'' software:
* [[Download cs-script.zip|zip/cs-script.zip]]. Extract it to "C:\Program Files". It will create a new directory called "C:\Program Files\cs-script".
* Next run "C:\Program Files\cs-script\css_config.exe". This will update system environment variables and bring up the configuration console. Press the "Close" button.
* To uninstall, run "C:\Program Files\cs-script\config.bat" and press 'Deactivate' button on the 'General' tab in the configuration console. It will undo any changes made by ~CS-Script installation.
3. (Optionally) Install a text editor that is good for code editing (recommendation: [[Notepad++|http://notepad-plus.sourceforge.net/uk/site.htm]].)
4. Save any of the examples below to your "c:\profile" directory, then at a DOS prompt run: "cscs.exe <filename>"
''Samples:'' ([[click here to download all samples|zip/CSSamples.zip]])
|''File''|''Description''|
|[[HelloWorld.cs|CS-Script/HelloWorld.cs]]|basic "Hello World" C# script demonstrating command-line arguments, getting input, printing out text, and returning an exit code|
|[[BasicK2Connection.cs|CS-Script/BasicK2Connection.cs]]|script showing each K2 connection steps. It shows you the basic calls you need to get started|
|[[K2ScriptTemplate.cs|CS-Script/K2ScriptTemplate.cs]]|a better K2 connection script template that encapsulates several of the connection calls in one connection object. makes for easier code reading.|
|[[GetSystemStatus.cs|CS-Script/GetSystemStatus.cs]]|get system status|
|[[LogMessage.cs|CS-Script/LogMessage.cs]]|log a message|
|[[GetListOfClips.cs|CS-Script/GetListOfClips.cs]]|gets a list of clips in a bin|
|[[PlayClip.cs|CS-Script/PlayClip.cs]]|play a clip|
|[[RecordClip.cs|CS-Script/RecordClip.cs]]|record a clip|
|[[ScheduledPlay.cs|CS-Script/ScheduledPlay.cs]]|play a clip at a scheduled time|
|[[ScheduledRecord.cs|CS-Script/ScheduledRecord.cs]]|record a clip at a scheduled time|
|[[CopyClip.cs|CS-Script/CopyClip.cs]]|copy a clip|
|[[DeleteClip.cs|CS-Script/DeleteClip.cs]]|delete a clip|
|[[TransferaClip.cs|CS-Script/TransferaClip.cs]]|transfer a clip|
|[[GetVersion.cs|CS-Script/GetVersion.cs]]|get system version information|
|[[GetDiskSpace.cs|CS-Script/GetDiskSpace.cs]]|get disk space|
|[[GetChannelOwner.cs|CS-Script/GetChannelOwner.cs]]|get channel owner information|
|[[GetAssetProperties.cs|CS-Script/GetAssetProperties.cs]]|get properties of a clip|
|[[GetChannelProperties.cs|CS-Script/GetChannelProperties.cs]]|get channel properties|
|[[CreateBin.cs|CS-Script/CreateBin.cs]]|create a bin|
|[[GetXmlChanges.cs|CS-Script/GetXmlChanges.cs]]|get XML change information|
|[[ClipCacheCalls.cs|CS-Script/ClipCacheCalls.cs]]|demonstrates calls needed to efficiently build a clip cache|
|[[EnumerateVolumes.cs|CS-Script/EnumerateVolumes.cs]]|enumerate the volumes|
|[[MonitorTransfers.cs|CS-Script/MonitorTransfers.cs]]|monitor transfers|
|[[PlayList.cs|CS-Script/PlayList.cs]]|create a play list|
|[[RecordAndRenameClip.cs|CS-Script/RecordAndRenameClip.cs]]|record clip, get editor, rename clip|
|[[viewmovie.cs|CS-Script/viewmovie.cs]]|view movie attributes|
|[[GetTimecode.cs|CS-Script/GetTimecode.cs]]|get the current timecode of a player|
|[[InsertEditTwoHead.cs|CS-Script/InsertEditTwoHead.cs]]|demonstrates how to do insert edits and use two heads for playback|
|[[PlayerCreateSubclip.cs|CS-Script/PlayerCreateSubclip.cs]]|create a subclip from a clip loaded in a player|
|[[MediaMgrCreateSubclip.cs|CS-Script/MediaMgrCreateSubclip.cs]]|create a subclip from a clip NOT loaded in a player|
|[[ImportDeleteTracks.cs|CS-Script/ImportDeleteTracks.cs]]|shows how to import, delete, move, and label tracks in a clip|
|[[AddAncillaryData.cs|CS-Script/AddAncillaryData.cs]]|shows how to merge, replace, and add ancillary data to a clip|
|[[LoadSaveConfig.cs|CS-Script/LoadSaveConfig.cs]]|shows how to load a configuration from or save a configuration to a local file.|
For the ~MediaMgr calls ~EnumerateVolumes, ~EnumerateBins, and ~EnumerateAssets, you need to call ~CloseResults on the cookie when you are finished using it. Your ~MediaMgr object only gets ten database selectors to work with. Everytime time you make an "Enumerate" call you use one of the selectors. Once you run out of selectors, your "Enumerate" calls will fail. You will also see “Too many open select statements: limit is 10” errors in the log. Calling ~CloseResults frees the selector and makes it usable to ~MediaMgr again.
Here is an example using ~EnumerateBins:
{{{
[C++]
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(m_bstrAppName, &spMediaMgr);
_bstr_t volume("V:");
long count = 0;
long cookie = 0;
hr = spMediaMgr->EnumerateBins(volume, &count, &cookie);
printf("hr = 0x%x, # of bins = %d\n\n", hr, count);
// ... loop code to GetXmlResults ...
// make sure your close the results!
hr = spMediaMgr->CloseResults(cookie);
}}}
See [[Get a list of clips]] for another enumeration example.
<<forEachTiddler
where
'tiddler.tags.contains("ChangeNotice")'
sortBy
'tiddler.data("date")'
descending
script
'function writeBugReportLine(tiddler, index)
{
var result ="";
if (index ==0)
{
result +="|background-color(#314184):!Date|background-color(#314184):!Title|background-color(#314184):!Build with Fix|\n";
}
result += "|" + tiddler.data("date") + "|" +
"[[" + tiddler.title + "]]|";
if ( tiddler.data("buildsWithFix") != null )
result += tiddler.data("buildsWithFix") + "|\n";
else
result += "|\n";
return result;
}'
write
'writeBugReportLine(tiddler,index)'
>>
<html>
<style>
.rolodex table {
border: 1px solid;
background-color:#DDDDDD;
}
.rolodex tr, .rolodex td {
border: 0px solid;
}
textarea{
font-family:Helvetica,sans-serif;
font-size:100%;
}
</style>
<span class="rolodex">
<table>
<tr>
<td colspan="6"><b>Change Notice</b></td>
</tr>
<tr>
<td colspan="2">Date (yyyy-mm-dd): <input name=date type=text style="width:40%" /></td>
</tr>
<tr>
<td colspan="6">Description:</td>
</tr>
<tr>
<td colspan="6">
<textarea name=description rows="10" cols="135" style="width:99%" ></textarea>
</td>
</tr>
<tr>
<td colspan="1">Builds with Fix:</td>
<td colspan="2">CheckIn Time/Branch: </td>
<td colspan="1">Associated CR's:</td>
</tr>
<tr>
<td colspan="1"><textarea name=buildsWithFix rows="3" cols="20" style="width:95%" ></textarea></td>
<td colspan="2"><textarea name=checkinTimeBranch rows="3" cols="20" style="width:95%" ></textarea></td>
<td colspan="1"><textarea name=assocCRs rows="3" cols="20" style="width:95%" ></textarea></td>
</tr>
</table>
</span>
</html>
|>|>|>|''Application [supplied] Status''|
|!Property|!Unmanaged Type|!Description|!Notes|
|text0|BSTR|Custom text string generally provided by the application that owns the channel||
|text1|BSTR|Custom text string generally provided by the application that owns the channel||
|text2|BSTR|Custom text string generally provided by the application that owns the channel||
|text3|BSTR|Custom text string generally provided by the application that owns the channel||
|statusTemplate|BSTR|Status templates string last set with a call to ~IChanStatus::put_~StatusTemplate||
|thumbnailStatus|BSTR|Indicates the status of the thumbnail provided by the application that owns the channel. Values include: Empty, New, Same, and Unknown. The status is initialized to “Unknown”. It is “New” just after a call to ~IChanStatus::~SetThumbnail. It is “Same” after the first time the thumbnail status is returned by this process. It is “Empty” after a call to ~IChanStatus::~ClearThumbnail.||
|thumbnail|BYTE SAFEARRAY|Jpeg thumbnail.||
|totalstorage|long|total disk space in ~MBytes||
|remainingstorage|long|remaining disk space in ~MBytes||
|pcntstorageremaining|long|percentage of disk space remaining||
|timeremainingstr|BSTR|time remaining on disk displayed as timecode value (depends on how channel is configured)||
|timeremaining|long|time remaining in fields (depends on how channel is configured)||
|>|>|>|''Channel Status''|
|!Property|!Unmanaged Type|!Description|!Notes|
|channelName|BSTR|Name of this channel (K2: C1, C2, C3, C4; ~MSeries: R1, P1, R2, P2)||
|channelType|BSTR|Values include Player, Recorder, and ~PlayerRecorder.||
|ownerName|BSTR|Name of the application that has allocated this channel||
|videoFormat|long|Channel’s configured video format. Enumerated values include: {{{Format525_60Hz_2To1 = 0, Format625_50Hz_2To1, Format720_59_94Hz_1To1, Format720_60Hz_1To1, Format1080_23_98Hz_1To1, Format1080_24Hz_1To1, Format1080_25Hz_1To1, Format1080_29_97Hz_1To1, Format1080_30Hz_1To1, Format1080_25Hz_2To1, Format1080_29_97Hz_2To1, Format1080_30Hz_2To1, Format1035_30Hz_2To1, Format1035_29_97Hz_2To1, VideoFormat_NA, Unknown}}}.||
|videoFormatStr|BSTR|String representation of channel’s configured video format. Values include: {{{Format525_60Hz_2To1, Format625_50Hz_2To1, Format720_59_94Hz_1To1, Format720_60Hz_1To1, Format1080_23_98Hz_1To1, Format1080_24Hz_1To1, Format1080_25Hz_1To1, Format1080_29_97Hz_1To1, Format1080_30Hz_1To1, Format1080_25Hz_2To1, Format1080_29_97Hz_2To1, Format1080_30Hz_2To1, Format1035_30Hz_2To1, Format1035_29_97Hz_2To1, VideoFormat_NA, Unknown.}}}||
|fieldsPerFrame|long|For interlaced video formats this value is 2. For progressive scan, the value is 1.||
|ganged|BOOL|whether or not the channel is ganged||
|gangindex|long|-1 = unganged, 0 = gang 1, 1 = gang 2||
|singlecontroller|BOOL|whether or not a single channel controls the gang||
|consolidateaudio|BOOL|whether or not to record audio from more than one channel||
|consolidatevideo|BOOL|whether or not to record video from more than one channel||
|>|>|>|''Asset Status''|
|!Property|!Unmanaged Type|!Description|!Notes|
|assetname|BSTR|Asset name.|
|assetUri|BSTR|Asset identifier in URI format. The URI string takes the following form: {{{edl/cmf//<machine>/<volume>/<bin>/<name>}}}||
|editName|BSTR|When the channel is playing a List asset, this is the name of the edit currently being played.||
|nextEditName|BSTR|When the channel is playing a List asset, this is the name of the next edit to be being played. This status value is currently always blank.||
|sectionName|BSTR|When the channel is playing a List asset, this is the name of the section currently being played.||
|nextSectionName|BSTR|When the channel is playing a List asset, this is the name of the next section to be being played. This status value is currently always blank.||
|>|>|>|''Timeline Status''|
|!Property|!Unmanaged Type|!Description|!Notes|
|durationStr|BSTR|Timecode representation of the channel timeline’s maxPos – minPos value. This is the amount of playable material on the timeline.|r|
|countdownStr|BSTR|Timecode representation of the maxPos – position value. This is the amount of play time left on the timeline.|r|
|percentComplete|float|Channel timeline’s maxPos – position expressed as a percent. If the timeline is being used for a crash record, then the percent complete gets to 100.0 every 10 seconds and starts over at 0.0.|r|
|position|long|Timeline position in fields. This value will always be 0 or greater.|r|
|portState|long|Enumerated value that indicates if the timeline is idle, cued for playback, cued for record, playing, or recording.|r|
|shuttleSpeed|float|Current rate. Normal play speed is 1.0.|r|
|sectionCountdownStr|BSTR|Timecode representation of the end-of-the-current-section – position value. This is the amount of play time left in the current section.|r|
|eventCountdownStr|BSTR|Timecode representation of the end-of-the-current-edit – position value. This is the amount of play time left in the current section.|r|
|sectionPercent|float|Channel timeline’s end-of-the-current-section – position value expressed as a percent.|r|
|>|>|>|''Video Status''|
|!Property|!Unmanaged Type|!Description|!Notes|
|fieldsize|long|Compressed size of the last played field in bytes.|r|
|pixCoding|BSTR|I, B, or P|r|
|>|>|>|''Audio Status (track 0)''|
|!Property|!Unmanaged Type|!Description|!Notes|
|inputGain0|float|Audio input gain value. This is used to adjust the input audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|outputGain0|float|Audio output gain value. This is used to adjust the output audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|audioLevel0|float|Current audio level after gain value has been applied. Units are in dBU.|r|
|>|>|''Audio Status (track 1)''|
|!Property|!Unmanaged Type|!Description|!Notes|
|inputGain1|float|Audio input gain value. This is used to adjust the input audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|outputGain1|float|Audio output gain value. This is used to adjust the output audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|audioLevel1|float|Current audio level after gain value has been applied. Units are in dBU.|r|
|>|>|>|''Timecode Status''|
|!Property|!Unmanaged Type|!Description|!Notes|
|timecodeStr|BSTR|When EE or recording, ~TimecodeStr is the status value from the first timecode resource’s current timecode source (VITC, LTC, or INT). When playing back, ~TimecodeStr represents the timecode media value.|r|
Notes:
r – Status is available in a range of values (~IChanStatus::~GetStatusRange)
''[[Click here|Basic Connection]]'' for example code on how to connect to ~AppService. See [[other code examples|Examples]], too.
[img[General AppService call sequence|images/CallSequence.png]]
''Client application ~AppService call sequence''
''Proxies:''
Client applications interact with the K2 via "proxy" objects. These are objects in the client application that represent the real objects in the K2. The proxy object's job is to simplify calls and to encapsulate house-keeping tasks so that the client application does not need to worry about it.
''Here are the basic steps for a client application to connect to a K2 and do work:''
''1. Connect to a K2's ~AppService:''
1a. Create an ~AppServerMgrProxy object in the client application.
1b. Tell it which K2 you want to connect to by calling the "~SetHost" function.
1c. Pass in user credentials by calling the "~SetUserCredentials" function.
1d. Call "Connect()". It returns true or false.
''2. Create an ~AppServer:''
Once connected, create an ~AppServer on the K2 by calling "~CreateAppServer". This will create an AppServer on the K2 and return an ~AppServerProxy object back to the client application.
''3. Create subsystem objects:''
Now that you have an ~AppServer for your client, create the subsystem object(s) that you need by calling "Create<Object>" (i.e. ~CreateController, ~CreateLog, etc.). This will create the object on the K2 and return an <Object>Proxy object to the client.
''4. Make K2 calls:''
Make calls on subsystem proxy objects to control or retrieve information from the K2.
''5. Cleanup:''
When finished, cleanup and delete the ~AppServer and subsystem objects by calling ~CloseConnection on the ~AppServerProxy.
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #def
SecondaryMid: #048
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
[[PageTemplate]]
|>|>|[[SiteTitle]] - [[SiteSubtitle]]|
|[[MainMenu]]|[[DefaultTiddlers]] <html><br><br><br><br></html>[[ViewTemplate]] <html><br><br></html> [[EditTemplate]]|[[SideBarOptions]]|
|~|~|[[OptionsPanel]]|
|~|~|[[AdvancedOptions]]|
|~|~|<<tiddler Configuration.SideBarTabs>>|
''StyleSheet:'' [[StyleSheetColors]] - [[StyleSheetLayout]] - [[StyleSheetPrint]]
[[SiteUrl]]
[[SideBarTabs]]
|[[TabTimeline]]|[[TabAll]]|[[TabTags]]|<<tiddler Configuration.TabMore>>|
|>|[[TabMore]]|
|TabMoreMissing|TabMoreOrphans|
Often you want to synchronize the playout or recording of a channel or multiple channels at a specific time. Context Triggers and Scheduled Context Conditions are used for this.
To schedule events to happen at specific triggers or conditions, issuing the following sequence of commands:
1. Cue the asset by calling a cue command. The clip must be cued before your can schedule or commit a change.
2. call "~BeginContextChange” command
3. make the calls you want to happen at the trigger or condition (i.e. Play() )
4. call "~CommitContextChange" or “~ScheduleContextChange” command.
''@@color(red):Note:@@'' see the [[C# script sample scripts section|CS-Script%20Sample%20Scripts]] and look at [[InsertEditTwoHead.cs|CS-Script/InsertEditTwoHead.cs]] for different Context Trigger and Scheduled Context examples.
!!Context Trigger types:
''"asap"'' – Used to apply a context change to the active context or a preview context. Committing with ASAP will make a preview context active. When cueing to a new position with ASAP, the decoder will wait until the correct picture is fetched.
''"now"'' – Used to apply a context change to the active context or a preview context. Committing with NOW will make a preview context active though, generally should only be used with the active context. When cueing to a new position with NOW, the decoder will immediately push out a picture, event if it is not the correct one. Use this context trigger when committing the first context change if you are not immediately playing. The still picture will pushed out sooner. Use the NOW context trigger when implementing a “scrub bar” and you want to push out a lot of pictures rapidly, so don’t care if they are the exact picture. Eventually the decoder will show the appropriate picture.
''"manual"'' – Used to apply a context change to a preview context without activating it. The preview context will never be activated if its last trigger was MANUAL.
''"follow"'' – Used to apply a context change to a preview context without activating it. When a preview context’s last trigger was FOLLOW, then it will automatically be activated when the active context reaches its end limit.
The code below illustrates how to play a second clip immediately after a clip:
{{{
// C# Code
// create a player and a background preview context
string channel = "C1";
bool isNewController = false;
ISimpleController controller = connection.AppServer.CreateController(
appName, channel, out isNewController);
ISimplePlayerRecorder player = (ISimplePlayerRecorder) controller;
ITwoHeadPlayerRecorder preview = (ITwoHeadPlayerRecorder) controller;
// load and start playing clip
player.Load("edl/cmf//local/V:/default/clip1");
player.Play();
// while main context is playing, load preview context with a second clip
preview.LoadPreview("edl/cmf//local/V:/default/clip2");
// setup a context change so that the second clip follows the first
preview.BeginPreviewContextChange();
preview.CueStart(); // cue to some position in clip
preview.Play();
// tell preview context to go active after primary context finishes (i.e. follow)
preview.CommitContextChange("follow");
// wait awhile...
Thread.Sleep(45000);
// stop and eject the clip
player.Stop();
player.Eject();
// close the channel
controller.CloseChannel();
}}}
!!Scheduled Context Conditions:
Scheduled context changes are always applied with an ASAP trigger when the scheduled condition is met. They include:
''"tickcount"'' tickValue (UINT) – A channel’s tickcount is incremented every field from the time it was started. The condition is met when the channel’s tickcount reaches the given value.
''"gpi"'' gpiInputMask (UINT) – The GPI condition is met when one or more GPI inputs indicated in the gpiInputMask are asserted. The GPI inputs in this mask must have been associated with the channel in the config utility.
''"timeofday"'' timecode (string) – This condition is met when the time of day clock value equals the given timecode string. This is useful when you want to schedule multiple channels to a single clock source. Use ~AppCenter's System Config panel for setting the ~TimeOfDay source.
''"vitc"'' timecode (string) – This condition is met when the VITC value on a channel equals the given timecode string.
''"ltc"'' timecode (string) – This condition is met when the LTC value on a channel equals the given timecode string.
''"ancvitc"'' timecode (string) – This condition is met when the ANCVITC value on a channel equals the given timecode string.
''"ancltc"'' timecode (string) – This condition is met when the ANCLTC value on a channel equals the given timecode string.
''"int"'' timecode (string) – This condition is met when the internal timecode source equals the timecode string.
''"gen"'' timecode (string) – Same as INT. This condition is met when the generated timecode source equals the timecode string.
''"gang"'' channelMask(UINT) – The context change is applied simultaneously to all channels indicated in the given channel mask. It is the application’s responsibility to have correctly set up all the channels appropriately.
''"transition"'' duration(int), videoTransition(int), audioTransition(int), matte(UINT) – This condition activates a preview context using the given transition parameters. It is also called a “Flying M/E”. The condition is an array of four values. This condition is only available on K2 Summit.
The code below illustrates how to start playing a clip at a specific timecode. Note: you can specify what the source of Time Of Day clock is for the K2 Client from ~AppCenter’s System Configuration panel. For instance you might set the Time Of Day clock to an LTC input. In this case also make sure that you have a valid LTC source connected to the K2 Client.
{{{
// C# Code
// create a controller
bool isNewController = false;
ISimpleController icontroller = connection.AppServer.CreateController(
appName, channel, out isNewController);
// cast it to a player recorder and load a clip
ISimplePlayerRecorder player = (ISimplePlayerRecorder) icontroller;
player.Load("edl/cmf//local/V:/default/Clip");
// must cue clip before you can schedule it!
player.CueStart();
// begin context change, telling it to play at a particular timecode
player.BeginContextChange();
player.Play();
player.ScheduleContextChange("timeofday", "09:00:00.00");
// to cancel schedule change
// player.CancelScheduledChange("timeofday", "09:00:00.00");
// wait awhile...
Thread.Sleep(45000);
// stop and eject the clip
player.Stop();
player.Eject();
// close the channel
icontroller.CloseChannel();
}}}
~ControlPoint PC system requirements are as follows:
|''Requirements''|''Comments''|
|Operating System|Microsoft Windows XP Professional, Service Pack 2 (Must be a U.S. version)|
|RAM|Minimum 512 MB, 1 GB recommended|
|Graphics acceleration|Must have at least 128M memory|
|Processor|Pentium 4 or higher class, 2 ~GHz or greater|
|Hard disk space|400 MB|
|Microsoft .NET Framework|Version 1.1 and 2.0|
|Java JRE|1.3.1_12 and 1.4.2_05 or higher. Required for HP Ethernet Switch Configuration interface, which is used for K2 Storage Systems (external storage).|
|XML|Microsoft XML 4 Service Pack 2 is required. You can install it from the msxml4sp2 file on the K2 System Software CD.|
The sample code below shows how to copy an asset. An ~AssetExists call is added to first make sure that the source clip exists.
{{{
[C#]
// create a mediamgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);
// if the clip does not exist, print error msg and return
if ( !mediaMgr.AssetExists("edl/cmf//local/V:/default/Clip") )
{
Console.WriteLine("Clip V:/default/Clip doesn't exist!");
return;
}
// copy clip
mediaMgr.CopyAsset("edl/cmf//local/V:/default/Clip",
"edl/cmf//local/V:/default/Clip_Copy");
// cleanup
mediaMgr.Dispose();
}}}
{{{
[C++]
// create a mediamgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(_bstr_t("YourApplicationName"),
&spMediaMgr);
// if the clip does not exist, print error msg and return
if ( !spMediaMgr->AssetExists( _bstr_t("edl/cmf//local/V:/default/Clip") )
{
printf("Clip V:/default/Clip doesn't exist!\n");
return;
}
// copy clip
hr = spMediaMgr->CopyAsset( _bstr_t("edl/cmf//local/V:/default/Clip"),
_bstr_t("edl/cmf//local/V:/default/Clip_Copy") );
}}}
The sample code below shows how to create a bin. Bins contain assets (clips, playlists, etc.)
{{{
[C#]
// create a mediamgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);
// create the bin V:/testbin
mediaMgr.CreateBin("V:", "testbin");
// cleanup
mediaMgr.Dispose();
}}}
{{{
[C++]
// create a mediamgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(_bstr_t("YourApplicationName"),
&spMediaMgr);
// create the bin V:/testbin
spAppServer->CreateBin( _bstr_t("V:"), _bstr_t("testbin") );
}}}
Below is example C# and C++ code for creating a basic playlist. This playlist has two sections that each contain two events. See [[Playlists, sections, and events|Playlists sections and events]] for an explanation on playlists.
A C# sample script can also be found on the [[C# Sample Scripts|CS-Script Sample Scripts]] page.
{{{
[C#]
// create a two-head player recorder
bool isNewController = false;
ITwoHeadPlayerRecorder player = (ITwoHeadPlayerRecorder) appServer.CreateController(
"YourApplicationName", "C1", out isNewController);
// set loop play mode to false
player.LoopPlayMode = false;
// enable list pauses
player.SetChannelProperty("listpauses", "true");
// enable list repeats
player.SetChannelProperty("listrepeats", "true");
// set current bin
player.SetCurrentBin("V:", "default");
// generate a unique name for the playlist (i.e. playlist, playlist_0, etc.)
string playlist = player.GenerateUniqueName ("playlist", "_");
// eject the play first to make sure we're clean
player.Eject();
// generate a new clip name (which also loads it)
player.New(playlist);
// get the editor
IEventEditor editor = (IEventEditor) player.GetEditor();
// insert the first section
string section1 = editor.InsertSection("");
// set the starting timecode
editor.SetStartingTimecode("01:00:00,00");
// insert several play events and get properties back
string editID_A = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipA", section1, 0, "");
object[] array = (object[]) editor.GetEventProperty (editID_A, "name+startpos+duration");
// your code to do something with event properties
string editID_B = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipB", section1, 0, "");
array = (object[]) editor.GetEventProperty (editID_B, "name+startpos+duration");
// your code to do something with event properties
// insert another section
string section2 = editor.InsertSection("");
// insert several more play events and get properties back
string editID_C = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipC", section2, 0, "");
array = (object[]) editor.GetEventProperty (editID_C, "name+startpos+duration");
// your code to do something with event properties
string editID_D = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipD", section2, 0, "");
array = (object[]) editor.GetEventProperty (editID_D, "name+startpos+duration");
// your code to do something with event properties
// play the list for awhile
player.Play();
Thread.Sleep(30000);
player.Stop();
// delete events
editor.DeleteEvent(editID_C);
editor.DeleteEvent(editID_D);
// delete section 2
editor.DeleteSection(section2);
// note: you cannot delete all sections. Since this is a playlist
// it will force you to keep at least one section in it.
// editor.DeleteSection(section2); // this would error
// eject the clip
player.Eject();
// close the channel
player.CloseChannel();
// cleanup
editor.Detach();
}}}
{{{
[C++]
short nIsNewController;
ISimpleControllerPtr spController;
_variant_t varChanNum(m_bstrChannel);
// create the controller and cast it to different interfaces
HRESULT hr = spAppServer->CreateController(_bstr_t("YourApplicationName"), _bstr_t("C1"),
&nIsNewController, &spController);
//printf("CreateController hresult = 0x%x\n", hr);
ISimplePlayerRecorderPtr spPlayer = (ISimplePlayerRecorderPtr) spController;
ITwoHeadPlayerRecorderPtr spTwoHeadPlayer = (ITwoHeadPlayerRecorderPtr) spController;
// set loop play mode to false
VARIANT_BOOL loopmode(FALSE);
hr = spPlayer->put_LoopPlayMode(loopmode);
// enable list pauses
hr = spController->SetChannelProperty(_bstr_t("listpauses"), _variant_t("true"));
// enable list repeats
hr = spController->SetChannelProperty(_bstr_t("listrepeats"), _variant_t("true"));
// set current bin
hr = spController->SetCurrentBin(_bstr_t("V:"), _bstr_t("default"));
// generate a unique name for the playlist (i.e. playlist, playlist_0, etc.)
BSTR playlist;
hr = spController->GenerateUniqueName (_bstr_t("playlist"), _bstr_t("_"), &playlist);
// eject the play first to make sure we're clean
hr = spPlayer->Eject();
// create the new playlist (which also loads it)
hr = spPlayer->New(playlist);
// get an editor and cast it to an event editor
ISimpleEditorPtr spEditor;
hr = spPlayer->GetEditor(&spEditor);
IEventEditorPtr spEventEditor = (IEventEditorPtr) spEditor;
// insert the first section
BSTR section1;
hr = spEventEditor->InsertSection(_bstr_t(""), §ion1);
// set the default starting timecode (this is what AppCenter does).
spEditor->SetStartingTimecode(_variant_t("01:00:00,00"));
// insert several play events and get properties back
BSTR editID_A;
VARIANT varResult;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipA"), section1, 0, _bstr_t(""), &editID_A);
hr = spEventEditor->GetEventProperty (editID_A, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties
BSTR editID_B;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipB"), section1, 0, _bstr_t(""), &editID_B);
hr = spEventEditor->GetEventProperty (editID_B, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties
// insert another section
BSTR section2;
hr = spEventEditor->InsertSection(_bstr_t(""), §ion2);
// insert several more play events and get properties back
BSTR editID_C;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipA"), section1, 0, _bstr_t(""), &editID_C);
hr = spEventEditor->GetEventProperty (editID_C, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties
BSTR editID_D;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipB"), section1, 0, _bstr_t(""), &editID_D);
hr = spEventEditor->GetEventProperty (editID_D, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties
// play for awhile
hr = spPlayer->Play();
Sleep(30000);
// eject
hr = spPlayer->Eject();
// close channel
hr = spController->CloseChannel();
// cleanup
hr = spEditor->Detach();
}}}
The code below shows how to create an ~AppServer. You will need to pass in a unique suite name and the name of your client application. See [[Suspending and Closing AppServers]] for an explanation of when an existing ~AppServer would be returned.
{{{
[C#]
bool newConnection = false;
IAppServer iappServer = appServerMgrProxy.CreateAppServer("YourSuiteName",
"YourClientApplicationName", out newConnection);
// ... do some work ...
iappServer.CloseConnection(); // or
iappServer.SuspendConnection();
}}}
{{{
[C++]
short nNewConnection;
IAppServerPtr spAppServer;
hr = spAppServerMgrProxy->CreateAppServer(_bstr_t("YourSuiteName",
_bstr_t("YourClientApplicationName"), &nNewConnection, &spAppServer);
// ... do some work ...
spAppServer->CloseConnection(); // or
spAppServer->SuspendConnection();
}}}
/***
|''Name:''|DataTiddlerPlugin|
|''Version:''|1.0.6 (2006-08-26)|
|''Source:''|http://tiddlywiki.abego-software.de/#DataTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).
Such tiddler data can be used in various applications. E.g. you may create tables that collect data from various tiddlers.
''//Example: "Table with all December Expenses"//''
{{{
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
}}}
//(This assumes that expenses are stored in tiddlers tagged with "expense".)//
<<forEachTiddler
where
'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
write
'"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
For other examples see DataTiddlerExamples.
''Access and Modify Tiddler Data''
You can "attach" data to every tiddler by assigning a JavaScript value (such as a string, boolean, number, or even arrays and compound objects) to named fields.
These values can be accessed and modified through the following Tiddler methods:
|!Method|!Example|!Description|
|{{{data(field)}}}|{{{t.data("age")}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{data(field,defaultValue)}}}|{{{t.data("isVIP",false)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{data()}}}|{{{t.data()}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{setData(field,value)}}}|{{{t.setData("age",42)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{setData(field,value,defaultValue)}}}|{{{t.setData("isVIP",flag,false)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
Alternatively you may use the following functions to access and modify the data. In this case the tiddler argument is either a tiddler or the name of a tiddler.
|!Method|!Description|
|{{{DataTiddler.getData(tiddler,field)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{DataTiddler.getData(tiddler,field,defaultValue)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{DataTiddler.getDataObject(tiddler)}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{DataTiddler.setData(tiddler,field,value)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{DataTiddler.setData(tiddler,field,value,defaultValue)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
//(For details on the various functions see the detailed comments in the source code.)//
''Data Representation in a Tiddler''
The data of a tiddler is stored as plain text in the tiddler's content/text, inside a "data" section that is framed by a {{{<data>...</data>}}} block. Inside the data section the information is stored in the [[JSON format|http://www.crockford.com/JSON/index.html]].
//''Data Section Example:''//
{{{
<data>{"isVIP":true,"user":"John Brown","age":34}</data>
}}}
The data section is not displayed when viewing the tiddler (see also "The showData Macro").
Beside the data section a tiddler may have all kind of other content.
Typically you will not access the data section text directly but use the methods given above. Nevertheless you may retrieve the text of the data section's content through the {{{DataTiddler.getDataText(tiddler)}}} function.
''Saving Changes''
The "setData" methods respect the "ForceMinorUpdate" and "AutoSave" configuration values. I.e. when "ForceMinorUpdate" is true changing a value using setData will not affect the "modifier" and "modified" attributes. With "AutoSave" set to true every setData will directly save the changes after a setData.
''Notifications''
No notifications are sent when a tiddler's data value is changed through the "setData" methods.
''Escape Data Section''
In case that you want to use the text {{{<data>}}} or {{{</data>}}} in a tiddler text you must prefix the text with a tilde ('~'). Otherwise it may be wrongly considered as the data section. The tiddler text {{{~<data>}}} is displayed as {{{<data>}}}.
''The showData Macro''
By default the data of a tiddler (that is stored in the {{{<data>...</data>}}} section of the tiddler) is not displayed. If you want to display this data you may used the {{{<<showData ...>>}}} macro:
''Syntax:''
|>|{{{<<}}}''showData '' [''JSON''] [//tiddlerName//] {{{>>}}}|
|''JSON''|By default the data is rendered as a table with a "Name" and "Value" column. When defining ''JSON'' the data is rendered in JSON format|
|//tiddlerName//|Defines the tiddler holding the data to be displayed. When no tiddler is given the tiddler containing the showData macro is used. When the tiddler name contains spaces you must quote the name (or use the {{{[[...]]}}} syntax.)|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
!Revision history
* v1.0.6 (2006-08-26)
** Removed misleading comment
* v1.0.5 (2006-02-27) (Internal Release Only)
** Internal
*** Make "JSLint" conform
* v1.0.4 (2006-02-05)
** Bugfix: showData fails in TiddlyWiki 2.0
* v1.0.3 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.2 (2005-12-22)
** Enhancements:
*** Handle texts "<data>" or "</data>" more robust when used in a tiddler text or as a field value.
*** Improved (JSON) error messages.
** Bugs fixed:
*** References are not updated when using the DataTiddler.
*** Changes to compound objects are not always saved.
*** "~</data>" is not rendered correctly (expected "</data>")
* v1.0.1 (2005-12-13)
** Features:
*** The showData macro supports an optional "tiddlername" argument to specify the tiddler containing the data to be displayed
** Bugs fixed:
*** A script immediately following a data section is deleted when the data is changed. (Thanks to GeoffS for reporting.)
* v1.0.0 (2005-12-12)
** initial version
!Code
***/
//{{{
//============================================================================
//============================================================================
// DataTiddlerPlugin
//============================================================================
//============================================================================
// Ensure that the DataTiddler Plugin is only installed once.
//
if (!version.extensions.DataTiddlerPlugin) {
version.extensions.DataTiddlerPlugin = {
major: 1, minor: 0, revision: 6,
date: new Date(2006, 7, 26),
type: 'plugin',
source: "http://tiddlywiki.abego-software.de/#DataTiddlerPlugin"
};
// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window;
if (!TiddlyWiki.prototype.getTiddler) {
TiddlyWiki.prototype.getTiddler = function(title) {
var t = this.tiddlers[title];
return (t !== undefined && t instanceof Tiddler) ? t : null;
};
}
//============================================================================
// DataTiddler Class
//============================================================================
// ---------------------------------------------------------------------------
// Configurations and constants
// ---------------------------------------------------------------------------
function DataTiddler() {
}
DataTiddler = {
// Function to stringify a JavaScript value, producing the text for the data section content.
// (Must match the implementation of DataTiddler.parse.)
//
stringify : null,
// Function to parse the text for the data section content, producing a JavaScript value.
// (Must match the implementation of DataTiddler.stringify.)
//
parse : null
};
// Ensure access for IE
window.DataTiddler = DataTiddler;
// ---------------------------------------------------------------------------
// Data Accessor and Mutator
// ---------------------------------------------------------------------------
// Returns the value of the given data field of the tiddler.
// When no such field is defined or its value is undefined
// the defaultValue is returned.
//
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.getData = function(tiddler, field, defaultValue) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.getTiddlerDataValue(t, field, defaultValue);
};
// Sets the value of the given data field of the tiddler to
// the value. When the value is equal to the defaultValue
// no value is set (and the field is removed)
//
// Changing data of a tiddler will not trigger notifications.
//
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.setData = function(tiddler, field, value, defaultValue) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler+ "("+t+")";
}
DataTiddler.setTiddlerDataValue(t, field, value, defaultValue);
};
// Returns the data object of the tiddler, with a property for every field.
//
// The properties of the returned data object may only be read and
// not be modified. To modify the data use DataTiddler.setData(...)
// or the corresponding Tiddler method.
//
// If no data section is defined a new (empty) object is returned.
//
// @param tiddler either a tiddler name or a Tiddler
//
DataTiddler.getDataObject = function(tiddler) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.getTiddlerDataObject(t);
};
// Returns the text of the content of the data section of the tiddler.
//
// When no data section is defined for the tiddler null is returned
//
// @param tiddler either a tiddler name or a Tiddler
// @return [may be null]
//
DataTiddler.getDataText = function(tiddler) {
var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
if (!(t instanceof Tiddler)) {
throw "Tiddler expected. Got "+tiddler;
}
return DataTiddler.readDataSectionText(t);
};
// ---------------------------------------------------------------------------
// Internal helper methods (must not be used by code from outside this plugin)
// ---------------------------------------------------------------------------
// Internal.
//
// The original JSONError is not very user friendly,
// especially it does not define a toString() method
// Therefore we extend it here.
//
DataTiddler.extendJSONError = function(ex) {
if (ex.name == 'JSONError') {
ex.toString = function() {
return ex.name + ": "+ex.message+" ("+ex.text+")";
};
}
return ex;
};
// Internal.
//
// @param t a Tiddler
//
DataTiddler.getTiddlerDataObject = function(t) {
if (t.dataObject === undefined) {
var data = DataTiddler.readData(t);
t.dataObject = (data) ? data : {};
}
return t.dataObject;
};
// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.getTiddlerDataValue = function(tiddler, field, defaultValue) {
var value = DataTiddler.getTiddlerDataObject(tiddler)[field];
return (value === undefined) ? defaultValue : value;
};
// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.setTiddlerDataValue = function(tiddler, field, value, defaultValue) {
var data = DataTiddler.getTiddlerDataObject(tiddler);
var oldValue = data[field];
if (value == defaultValue) {
if (oldValue !== undefined) {
delete data[field];
DataTiddler.save(tiddler);
}
return;
}
data[field] = value;
DataTiddler.save(tiddler);
};
// Internal.
//
// Reads the data section from the tiddler's content and returns its text
// (as a String).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readDataSectionText = function(tiddler) {
var matches = DataTiddler.getDataTiddlerMatches(tiddler);
if (matches === null || !matches[2]) {
return null;
}
return matches[2];
};
// Internal.
//
// Reads the data section from the tiddler's content and returns it
// (as an internalized object).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readData = function(tiddler) {
var text = DataTiddler.readDataSectionText(tiddler);
try {
return text ? DataTiddler.parse(text) : null;
} catch(ex) {
throw DataTiddler.extendJSONError(ex);
}
};
// Internal.
//
// Returns the serialized text of the data of the given tiddler, as it
// should be stored in the data section.
//
// @param tiddler a Tiddler
//
DataTiddler.getDataTextOfTiddler = function(tiddler) {
var data = DataTiddler.getTiddlerDataObject(tiddler);
return DataTiddler.stringify(data);
};
// Internal.
//
DataTiddler.indexOfNonEscapedText = function(s, subString, startIndex) {
var index = s.indexOf(subString, startIndex);
while ((index > 0) && (s[index-1] == '~')) {
index = s.indexOf(subString, index+1);
}
return index;
};
// Internal.
//
DataTiddler.getDataSectionInfo = function(text) {
// Special care must be taken to handle "<data>" and "</data>" texts inside
// a data section.
// Also take care not to use an escaped <data> (i.e. "~<data>") as the start
// of a data section. (Same for </data>)
// NOTE: we are explicitly searching for a data section that contains a JSON
// string, i.e. framed with braces. This way we are little bit more robust in
// case the tiddler contains unescaped texts "<data>" or "</data>". This must
// be changed when using a different stringifier.
var startTagText = "<data>{";
var endTagText = "}</data>";
var startPos = 0;
// Find the first not escaped "<data>".
var startDataTagIndex = DataTiddler.indexOfNonEscapedText(text, startTagText, 0);
if (startDataTagIndex < 0) {
return null;
}
// Find the *last* not escaped "</data>".
var endDataTagIndex = text.indexOf(endTagText, startDataTagIndex);
if (endDataTagIndex < 0) {
return null;
}
var nextEndDataTagIndex;
while ((nextEndDataTagIndex = text.indexOf(endTagText, endDataTagIndex+1)) >= 0) {
endDataTagIndex = nextEndDataTagIndex;
}
return {
prefixEnd: startDataTagIndex,
dataStart: startDataTagIndex+(startTagText.length)-1,
dataEnd: endDataTagIndex,
suffixStart: endDataTagIndex+(endTagText.length)
};
};
// Internal.
//
// Returns the "matches" of a content of a DataTiddler on the
// "data" regular expression. Return null when no data is defined
// in the tiddler content.
//
// Group 1: text before data section (prefix)
// Group 2: content of data section
// Group 3: text behind data section (suffix)
//
// @param tiddler a Tiddler
// @return [may be null] null when the tiddler contains no data section, otherwise see above.
//
DataTiddler.getDataTiddlerMatches = function(tiddler) {
var text = tiddler.text;
var info = DataTiddler.getDataSectionInfo(text);
if (!info) {
return null;
}
var prefix = text.substr(0,info.prefixEnd);
var data = text.substr(info.dataStart, info.dataEnd-info.dataStart+1);
var suffix = text.substr(info.suffixStart);
return [text, prefix, data, suffix];
};
// Internal.
//
// Saves the data in a <data> block of the given tiddler (as a minor change).
//
// The "chkAutoSave" and "chkForceMinorUpdate" options are respected.
// I.e. the TiddlyWiki *file* is only saved when AutoSave is on.
//
// Notifications are not send.
//
// This method should only be called when the data really has changed.
//
// @param tiddler
// the tiddler to be saved.
//
DataTiddler.save = function(tiddler) {
var matches = DataTiddler.getDataTiddlerMatches(tiddler);
var prefix;
var suffix;
if (matches === null) {
prefix = tiddler.text;
suffix = "";
} else {
prefix = matches[1];
suffix = matches[3];
}
var dataText = DataTiddler.getDataTextOfTiddler(tiddler);
var newText =
(dataText !== null)
? prefix + "<data>" + dataText + "</data>" + suffix
: prefix + suffix;
if (newText != tiddler.text) {
// make the change in the tiddlers text
// ... see DataTiddler.MyTiddlerChangedFunction
tiddler.isDataTiddlerChange = true;
// ... do the action change
tiddler.set(
tiddler.title,
newText,
config.options.txtUserName,
config.options.chkForceMinorUpdate? undefined : new Date(),
tiddler.tags);
// ... see DataTiddler.MyTiddlerChangedFunction
delete tiddler.isDataTiddlerChange;
// Mark the store as dirty.
store.dirty = true;
// AutoSave if option is selected
if(config.options.chkAutoSave) {
saveChanges();
}
}
};
// Internal.
//
DataTiddler.MyTiddlerChangedFunction = function() {
// Remove the data object from the tiddler when the tiddler is changed
// by code other than DataTiddler code.
//
// This is necessary since the data object is just a "cached version"
// of the data defined in the data section of the tiddler and the
// "external" change may have changed the content of the data section.
// Thus we are not sure if the data object reflects the data section
// contents.
//
// By deleting the data object we ensure that the data object is
// reconstructed the next time it is needed, with the data defined by
// the data section in the tiddler's text.
// To indicate that a change is a "DataTiddler change" a temporary
// property "isDataTiddlerChange" is added to the tiddler.
if (this.dataObject && !this.isDataTiddlerChange) {
delete this.dataObject;
}
// call the original code.
DataTiddler.originalTiddlerChangedFunction.apply(this, arguments);
};
//============================================================================
// Formatters
//============================================================================
// This formatter ensures that "~<data>" is rendered as "<data>". This is used to
// escape the "<data>" of a data section, just in case someone really wants to use
// "<data>" as a text in a tiddler and not start a data section.
//
// Same for </data>.
//
config.formatters.push( {
name: "data-escape",
match: "~<\\/?data>",
handler: function(w) {
w.outputText(w.output,w.matchStart + 1,w.nextMatch);
}
} );
// This formatter ensures that <data>...</data> sections are not rendered.
//
config.formatters.push( {
name: "data",
match: "<data>",
handler: function(w) {
var info = DataTiddler.getDataSectionInfo(w.source);
if (info && info.prefixEnd == w.matchStart) {
w.nextMatch = info.suffixStart;
} else {
w.outputText(w.output,w.matchStart,w.nextMatch);
}
}
} );
//============================================================================
// Tiddler Class Extension
//============================================================================
// "Hijack" the changed method ---------------------------------------------------
DataTiddler.originalTiddlerChangedFunction = Tiddler.prototype.changed;
Tiddler.prototype.changed = DataTiddler.MyTiddlerChangedFunction;
// Define accessor methods -------------------------------------------------------
// Returns the value of the given data field of the tiddler. When no such field
// is defined or its value is undefined the defaultValue is returned.
//
// When field is undefined (or null) the data object is returned. (See
// DataTiddler.getDataObject.)
//
// @param field [may be null, undefined]
// @param defaultValue [may be null, undefined]
// @return [may be null, undefined]
//
Tiddler.prototype.data = function(field, defaultValue) {
return (field)
? DataTiddler.getTiddlerDataValue(this, field, defaultValue)
: DataTiddler.getTiddlerDataObject(this);
};
// Sets the value of the given data field of the tiddler to the value. When the
// value is equal to the defaultValue no value is set (and the field is removed).
//
// @param value [may be null, undefined]
// @param defaultValue [may be null, undefined]
//
Tiddler.prototype.setData = function(field, value, defaultValue) {
DataTiddler.setTiddlerDataValue(this, field, value, defaultValue);
};
//============================================================================
// showData Macro
//============================================================================
config.macros.showData = {
// Standard Properties
label: "showData",
prompt: "Display the values stored in the data section of the tiddler"
};
config.macros.showData.handler = function(place,macroName,params) {
// --- Parsing ------------------------------------------
var i = 0; // index running over the params
// Parse the