I want to put an image overlay over video, but I'm not sure how I can do this. I'm trying to modify example from this repo Azure Media Services v3 .NET Core tutorials
, bascially what I changed here is transform:
private static Transform EnsureTransformForOverlayExists(IAzureMediaServicesClient client, string resourceGroupName, string accountName, string transformNameNew)
{
Console.WriteLine(transformNameNew);
Transform transform = client.Transforms.Get(resourceGroupName, accountName, transformName);
if (transform == null)
{
TransformOutput[] outputs = new TransformOutput[]
{
new TransformOutput(
new StandardEncoderPreset(
codecs: new Codec[]
{
new AacAudio(
channels: 2,
samplingRate: 48000,
bitrate: 128000,
profile: AacAudioProfile.AacLc
),
new H264Video(stretchMode: "AutoFit",
keyFrameInterval: TimeSpan.FromSeconds(2),
layers: new[]
{
new H264Layer(
bitrate: 1500000,
maxBitrate: 1500000,
width: "640",
height: "360"
)
}
),
new PngImage(
start: "25%",
step: "25%",
range: "80%",
layers: new PngLayer[]{
new PngLayer(
width: "50%",
height: "50%"
)
}
),
},
filters: new Filters
{
Overlays = new List<Overlay>
{
new VideoOverlay("input1")
}
},
formats: new Format[]
{
new Mp4Format(
filenamePattern: "{Basename}_letterbox{Extension}"
),
new PngFormat(
filenamePattern: "{Basename}_{Index}_{Label}_{Extension}"
),
}
))
};
transform = client.Transforms.CreateOrUpdate(resourceGroupName, accountName, transformName, outputs);
}
return transform;
}
and RunAsync method to provide multiple inputs where one of them should be an overlay:
private static async Task RunAsync(ConfigWrapper config)
{
IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config);
// Set the polling interval for long running operations to 2 seconds.
// The default value is 30 seconds for the .NET client SDK
client.LongRunningOperationRetryTimeout = 2;
try
{
// Ensure that you have customized encoding Transform. This is really a one time setup operation.
Transform overlayTransform = EnsureTransformForOverlayExists(client, config.ResourceGroup, config.AccountName, transformName);
// Creating a unique suffix so that we don't have name collisions if you run the sample
// multiple times without cleaning up.
string uniqueness = Guid.NewGuid().ToString().Substring(0, 13);
string jobName = "job-" + uniqueness;
string inputAssetName = "input-" + uniqueness;
string outputAssetName = "output-" + uniqueness;
Asset asset = client.Assets.CreateOrUpdate(config.ResourceGroup, config.AccountName, inputAssetName, new Asset());
var inputs = new JobInputs(new List<JobInput>());
var input = new JobInputHttp(
baseUri: "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/",
files: new List<String> {"Ignite-short.mp4"},
label:"input1"
);
inputs.Inputs.Add((input));
input = new JobInputHttp(
baseUri: "SomeBaseUriHere",
files: new List<string> {"AssetVideo_000001_None_.png"},
label: "overlay");
inputs.Inputs.Add((input));
Asset outputAsset = CreateOutputAsset(client, config.ResourceGroup, config.AccountName, outputAssetName);
Job job = SubmitJob(client, config.ResourceGroup, config.AccountName, transformName, jobName, inputs, outputAsset.Name);
DateTime startedTime = DateTime.Now;
job = WaitForJobToFinish(client, config.ResourceGroup, config.AccountName, transformName, jobName);
TimeSpan elapsed = DateTime.Now - startedTime;
if (job.State == JobState.Finished)
{
Console.WriteLine("Job finished.");
if (!Directory.Exists(outputFolder))
Directory.CreateDirectory(outputFolder);
await MakeContainerPublic(client, config.ResourceGroup, config.AccountName, outputAsset.Name, config.BlobConnectionString);
DownloadResults(client, config.ResourceGroup, config.AccountName, outputAsset.Name, outputFolder).Wait();
}
else if (job.State == JobState.Error)
{
Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
Console.WriteLine($"ERROR: error details: {job.Outputs[0].Error.Details[0].Message}");
}
}
catch(ApiErrorException ex)
{
string code = ex.Body.Error.Code;
string message = ex.Body.Error.Message;
Console.WriteLine("ERROR:API call failed with error code: {0} and message: {1}", code, message);
}
}
But I have this error
Microsoft.Cloud.Media.Encoding.PresetException: Preset ERROR: There are 2 input assets. Preset has 2 Source but does NOT specify AssetID for each Source
and I have no idea how to overcome this.
At this time, it is not possible to use v3 APIs to create overlays. The feature is not fully implemented. See this link for other gaps between the v2 and v3 APIs.
For more details, you can see this site .
Related
Is it possible to migrate users using the MS Graph API in Azure AD?
If so, please explain how to migrate users from one tenant to the other using the MS Graph API.
You can export the users with MS Graph. Note, you can't export the passwords. This means that you have to create a new password and share it with the users. Or choose a random password and let the users reset their password using the self-service password rest feature.
Here is an example how to export the users from a directly
public static async Task ListUsers(GraphServiceClient graphClient)
{
Console.WriteLine("Getting list of users...");
DateTime startTime = DateTime.Now;
Dictionary<string, string> usersCollection = new Dictionary<string, string>();
int page = 0;
try
{
// Get all users
var users = await graphClient.Users
.Request()
.Select(e => new
{
e.DisplayName,
e.Id
}).OrderBy("DisplayName")
.GetAsync();
// Iterate over all the users in the directory
var pageIterator = PageIterator<User>
.CreatePageIterator(
graphClient,
users,
// Callback executed for each user in the collection
(user) =>
{
usersCollection.Add(user.DisplayName, user.Id);
return true;
},
// Used to configure subsequent page requests
(req) =>
{
var d = DateTime.Now - startTime;
Console.WriteLine($"{string.Format(TIME_FORMAT, d.Days, d.Hours, d.Minutes, d.Seconds)} users: {usersCollection.Count}");
// Set a variable to the Documents path.
string filePrefix = "0";
if (usersCollection.Count >= 1000000)
{
filePrefix = usersCollection.Count.ToString()[0].ToString();
}
page++;
if (page >= 50)
{
page = 0;
string docPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), $"users_{filePrefix}.json");
System.IO.File.WriteAllTextAsync(docPath, JsonSerializer.Serialize(usersCollection));
}
Thread.Sleep(200);
return req;
}
);
await pageIterator.IterateAsync();
// Write last page
string docPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), $"users_all.json");
System.IO.File.WriteAllTextAsync(docPath, JsonSerializer.Serialize(usersCollection));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
After you export the users, you can import them back to the other tenant. The following example creates test users. Change the code to set the values from the files you exported earlier. Also, this code uses batch with 20 users in single operation.
public static async Task CreateTestUsers(GraphServiceClient graphClient, AppSettings appSettings, bool addMissingUsers)
{
Console.Write("Enter the from value: ");
int from = int.Parse(Console.ReadLine()!);
Console.Write("Enter the to value: ");
int to = int.Parse(Console.ReadLine()!);
int count = 0;
Console.WriteLine("Starting create test users operation...");
DateTime startTime = DateTime.Now;
Dictionary<string, string> existingUsers = new Dictionary<string, string>();
// Add the missing users
if (addMissingUsers)
{
// Set a variable to the Documents path.
string docPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "users.json");
if (!System.IO.File.Exists(docPath))
{
Console.WriteLine("Can't find the '{docPath}' file.");
}
string usersFile = System.IO.File.ReadAllText(docPath);
existingUsers = JsonSerializer.Deserialize<Dictionary<string, string>>(usersFile);
if (existingUsers == null)
{
Console.WriteLine("Can't deserialize users");
return;
}
Console.WriteLine($"There are {existingUsers.Count} in the directory");
}
List<User> users = new List<User>();
// The batch object
var batchRequestContent = new BatchRequestContent();
for (int i = from; i < to; i++)
{
// 1,000,000
string ID = TEST_USER_PREFIX + i.ToString().PadLeft(7, '0');
if (addMissingUsers)
{
if (existingUsers.ContainsKey(ID))
continue;
}
count++;
try
{
var user = new User
{
DisplayName = ID,
JobTitle = ID.Substring(ID.Length - 1),
Identities = new List<ObjectIdentity>()
{
new ObjectIdentity
{
SignInType = "userName",
Issuer = appSettings.TenantName,
IssuerAssignedId = ID
},
new ObjectIdentity
{
SignInType = "emailAddress",
Issuer = appSettings.TenantName,
IssuerAssignedId = $"{ID}#{TEST_USER_SUFFIX}"
}
},
PasswordProfile = new PasswordProfile
{
Password = "1",
ForceChangePasswordNextSignIn = false
},
PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword"
};
users.Add(user);
if (addMissingUsers)
{
Console.WriteLine($"Adding missing {ID} user");
}
// POST requests are handled a bit differently
// The SDK request builders generate GET requests, so
// you must get the HttpRequestMessage and convert to a POST
var jsonEvent = graphClient.HttpProvider.Serializer.SerializeAsJsonContent(user);
HttpRequestMessage addUserRequest = graphClient.Users.Request().GetHttpRequestMessage();
addUserRequest.Method = HttpMethod.Post;
addUserRequest.Content = jsonEvent;
if (batchRequestContent.BatchRequestSteps.Count >= BATCH_SIZE)
{
var d = DateTime.Now - startTime;
Console.WriteLine($"{string.Format(TIME_FORMAT, d.Days, d.Hours, d.Minutes, d.Seconds)}, count: {count}, user: {ID}");
// Run sent the batch requests
var returnedResponse = await graphClient.Batch.Request().PostAsync(batchRequestContent);
// Dispose the HTTP request and empty the batch collection
foreach (var step in batchRequestContent.BatchRequestSteps) ((BatchRequestStep)step.Value).Request.Dispose();
batchRequestContent = new BatchRequestContent();
}
// Add the event to the batch operations
batchRequestContent.AddBatchRequestStep(addUserRequest);
// Console.WriteLine($"User '{user.DisplayName}' successfully created.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
I am working on Android SMS app. Part of it has to fetch inbox data (address, date, body) and fill list view. This works fine through following code :
public void btnInboxOnClick {
// Create Inbox box URI
Uri inboxURI = Uri.parse("content://sms/inbox");
// List required columns
String[] reqCols = new String[] { "_id", "address", "body", "date" };
// Get Content Resolver object, which will deal with Content
// Provider
ContentResolver cr = getContentResolver();
// Fetch Inbox SMS Message from Built-in Content Provider
Cursor c = cr.query(inboxURI, reqCols, null, null, null);
// Attached Cursor with adapter and display in listview
adapter = new SimpleCursorAdapter(this, R.layout.row, c,
new String[] { "body", "address", "date" }, new int[] {
R.id.lblMsg, R.id.lblNumber, R.id.lblDate });
lvMsg.setAdapter(adapter);
I want to insert function call to display meaningful date-time string instead of milliseconds number fetched from db. My function code:
public static String millisToDate(String TimeMillis) {
String finalDate;
long tm = Long.parseLong(TimeMillis);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(tm);
Date date = calendar.getTime();
SimpleDateFormat outputFormat = new SimpleDateFormat("MMM-dd-yyyy HH:mm");
finalDate = outputFormat.format(date);
return finalDate;
}
Any attempt to call function compile, but app crashes. How should I connect function?
This answer was helpful too:
Code looks like this:
public void btnInboxOnClick {
// Create Inbox box URI
Uri inboxURI = Uri.parse("content://sms/inbox");
// List required columns
String[] reqCols = new String[] { "_id", "address", "body", "date" };
// Get Content Resolver object, which will deal with Content
// Provider
ContentResolver cr = getContentResolver();
// Fetch Inbox SMS Message from Built-in Content Provider
Cursor c = cr.query(inboxURI, reqCols, null, null, null);
// Attached Cursor with adapter and display in listview
adapter = new SimpleCursorAdapter(this, R.layout.row, c,
new String[] { "body", "address", "date" }, new int[] {
R.id.lblMsg, R.id.lblNumber, R.id.lblDate });
adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View aView, Cursor aCursor, int aColumnIndex) {
if (aColumnIndex == aCursor.getColumnIndex("date")) {
String createDate = aCursor.getString(aColumnIndex);
TextView textView = (TextView) aView;
textView.setText(millisToDate(createDate));
return true;
}
return false;
}
});
lvMsg.setAdapter(adapter);
public static String millisToDate(String TimeMillis) {
String finalDate;
long tm = Long.parseLong(TimeMillis);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(tm);
Date date = calendar.getTime();
SimpleDateFormat outputFormat = new SimpleDateFormat("MMM-dd-yyyy HH:mm");
finalDate = outputFormat.format(date);
return finalDate;
}
I am trying to build a query parser by ranking the passages containing the terms.
I understand that I need to use SpanNearQuery, but I can't find a way to access Spans even after going through the documentation. The method I got returns null.
I have read https://lucidworks.com/blog/2009/07/18/the-spanquery/ which explains in a good way about the query. This explains how to access spans, but it is for solr 4.0 and unfortunately solr 6.3 doesn't have atomic reader any more.
How can I get the actual spans?
public void process(ResponseBuilder rb) throws IOException {
SolrParams params = rb.req.getParams();
log.warn("in Process");
if (!params.getBool(COMPONENT_NAME, false)) {
return;
}
Query origQuery = rb.getQuery();
// TODO: longer term, we don't have to be a span query, we could re-analyze the document
if (origQuery != null) {
if (origQuery instanceof SpanNearQuery == false) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Illegal query type. The incoming query must be a Lucene SpanNearQuery and it was a " + origQuery.getClass().getName());
}
SpanNearQuery sQuery = (SpanNearQuery) origQuery;
SolrIndexSearcher searcher = rb.req.getSearcher();
IndexReader reader = searcher.getIndexReader();
log.warn("before leaf reader context");
List<LeafReaderContext> ctxs = (List<LeafReaderContext>) reader.leaves();
log.warn("after leaf reader context");
LeafReaderContext ctx = ctxs.get(0);
SpanWeight spanWeight = sQuery.createWeight(searcher, true);
Spans spans = spanWeight.getSpans(ctx, SpanWeight.Postings.POSITIONS);
AtomicReader wrapper = SlowCompositeReaderWrapper.wrap(reader);
Map<Term, TermContext> termContexts = new HashMap<Term, TermContext>();
Spans spans = fleeceQ.getSpans(wrapper.getContext(), new Bits.MatchAllBits(reader.numDocs()), termContexts);
// SpanWeight.Postings[] postings= SpanWeight.Postings.values();
// Spans spans = sQuery.getSpans();
// Assumes the query is a SpanQuery
// Build up the query term weight map and the bi-gram
Map<String, Float> termWeights = new HashMap<String, Float>();
Map<String, Float> bigramWeights = new HashMap<String, Float>();
createWeights(params.get(CommonParams.Q), sQuery, termWeights, bigramWeights, reader);
float adjWeight = params.getFloat(ADJACENT_WEIGHT, DEFAULT_ADJACENT_WEIGHT);
float secondAdjWeight = params.getFloat(SECOND_ADJ_WEIGHT, DEFAULT_SECOND_ADJACENT_WEIGHT);
float bigramWeight = params.getFloat(BIGRAM_WEIGHT, DEFAULT_BIGRAM_WEIGHT);
// get the passages
int primaryWindowSize = params.getInt(OWLParams.PRIMARY_WINDOW_SIZE, DEFAULT_PRIMARY_WINDOW_SIZE);
int adjacentWindowSize = params.getInt(OWLParams.ADJACENT_WINDOW_SIZE, DEFAULT_ADJACENT_WINDOW_SIZE);
int secondaryWindowSize = params.getInt(OWLParams.SECONDARY_WINDOW_SIZE, DEFAULT_SECONDARY_WINDOW_SIZE);
WindowBuildingTVM tvm = new WindowBuildingTVM(primaryWindowSize, adjacentWindowSize, secondaryWindowSize);
PassagePriorityQueue rankedPassages = new PassagePriorityQueue();
// intersect w/ doclist
DocList docList = rb.getResults().docList;
log.warn("Before Spans");
while (spans.nextDoc() != Spans.NO_MORE_DOCS) {
// build up the window
log.warn("Iterating through spans");
if (docList.exists(spans.docID())) {
tvm.spanStart = spans.startPosition();
tvm.spanEnd = spans.endPosition();
// tvm.terms
Terms terms = reader.getTermVector(spans.docID(), sQuery.getField());
tvm.map(terms, spans);
// The entries map contains the window, do some ranking of it
if (tvm.passage.terms.isEmpty() == false) {
log.debug("Candidate: Doc: {} Start: {} End: {} ", new Object[] { spans.docID(), spans.startPosition(), spans.endPosition() });
}
tvm.passage.lDocId = spans.docID();
tvm.passage.field = sQuery.getField();
// score this window
try {
addPassage(tvm.passage, rankedPassages, termWeights, bigramWeights, adjWeight, secondAdjWeight, bigramWeight);
} catch (CloneNotSupportedException e) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Internal error cloning Passage", e);
}
// clear out the entries for the next round
tvm.passage.clear();
}
}
}
}
I am building a simple application for windows phone. I want to create a database and want to have several (lets say 10) items in the database. I am a beginner and every tutorial that i have seen is sth about adding items in the database on button "add" or sth like that. I don't need that, because I want to have several item that are in the database, ready for the user to use them. How can I achieve this? Please write to me in a clear way, because I am still a beginner. If you can provide some links of examples or tutorials, that would be great. Thank u!
If you need to have the preloaded DB then you can Add a sqlCe DB in your application and populate the db with your seed Data.
then you can copy the DB file to your ISO Store while your Constructor of DBContext is invoked.
public Moviadb1DataContext (string connectionString) : base(connectionString)
{
IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication();
if (!iso.FileExists("Moviadb1.sdf"))
{
MoveReferenceDatabase();
}
if (!DatabaseExists())
CreateDatabase();
}
public static void MoveReferenceDatabase()
{
// Obtain the virtual store for the application.
IsolatedStorageFile iso = IsolatedStorageFile.GetUserStoreForApplication();
// Create a stream for the file in the installation folder.
using (Stream input = Application.GetResourceStream(new Uri("Moviadb1.sdf", UriKind.Relative)).Stream)
{
// Create a stream for the new file in isolated storage.
using (IsolatedStorageFileStream output = iso.CreateFile("Moviadb1.sdf"))
{
// Initialize the buffer.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the file from the installation folder to isolated storage.
while ((bytesRead = input.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
output.Write(readBuffer, 0, bytesRead);
}
}
}
}
you can also add some seed data instead of moving the reference DB if you have very small amount of Data.
public ListenDataDataContext (string connectionString) : base(connectionString)
{
if (!DatabaseExists())
{
CreateDatabase();
List<Audiables> PreLoads = new List<Audiables>();
PreLoads.Add(new Audiables { Category = 1, Name = "I want To Eat", AudioLocation = "Sounds/Food/1_IwantToEat.wma", ImageLocation = "Images/Food/1_IwantToEat.jpg" });
PreLoads.Add(new Audiables { Category = 1, Name = "I want To Drink", AudioLocation = "Sounds/Food/1_IwantToDrink.wma", ImageLocation = "Images/Food/1_IwantToDrink.jpg" });
PreLoads.Add(new Audiables { Category = 2, Name = "I want A Ticket", AudioLocation = "Sounds/Travel/1_IwantATicket.wma", ImageLocation = "Images/Travel/1_IwantATicket.jpg" });
PreLoads.Add(new Audiables { Category = 2, Name = "I want To Sit", AudioLocation = "Sounds/Travel/1_IwantToSit.wma", ImageLocation = "Images/Travel/1_IwantToSit.jpg" });
PreLoads.Add(new Audiables { Category = 3, Name = "How Much Is That", AudioLocation = "Sounds/Shopping/1_HowMuchIsThat.wma", ImageLocation = "Images/Shopping/1_HowMuchIsThat.jpg" });
PreLoads.Add(new Audiables { Category = 3, Name = "Please Take the Money", AudioLocation = "Sounds/Shopping/1_PleaseTakeTheMoney.wma", ImageLocation = "Images/Shopping/1_PleaseTakeTheMoney.jpg" });
Audiables.InsertAllOnSubmit(PreLoads);
this.SubmitChanges();
}
}
Happy app making :)
Best way is to check the "Local Database Sample" in the Windows Phone Code Samples!
Is there a working example anywhere of how to create a meeting request using EWS for Exchange 2007 using C#? Which properties are required? I have added a web service reference and can connect to create and send various items but keep getting the error "Set action is invalid for property." on the response messages. It never says what property is invalid
var ews = new ExchangeServiceBinding {
Credentials = new NetworkCredential("user", "pass"),
Url = "https://servername/ews/exchange.asmx",
RequestServerVersionValue = new RequestServerVersion {
Version = ExchangeVersionType.Exchange2007}
};
var startDate = new DateTime(2010, 9, 18, 16, 00, 00);
var meeting = new CalendarItemType {
IsMeeting = true,
IsMeetingSpecified = true,
Subject = "test EWS",
Body = new BodyType {Value = "test body", BodyType1 = BodyTypeType.HTML},
Start = startDate,
StartSpecified = true,
End = startDate.AddHours(1),
EndSpecified = true,
MeetingTimeZone = new TimeZoneType{
TimeZoneName = TimeZone.CurrentTimeZone.StandardName, BaseOffset = "PT0H"},
Location = "Meeting",
RequiredAttendees = new [] {
new AttendeeType{Mailbox =new EmailAddressType{
EmailAddress ="test1#domain.com",RoutingType = "SMTP"}},
new AttendeeType{Mailbox =new EmailAddressType{
EmailAddress ="test2#domain.com",RoutingType = "SMTP"}}
}
};
var request = new CreateItemType {
SendMeetingInvitations =
CalendarItemCreateOrDeleteOperationType.SendToAllAndSaveCopy,
SendMeetingInvitationsSpecified = true,
SavedItemFolderId = new TargetFolderIdType{Item = new DistinguishedFolderIdType{
Id=DistinguishedFolderIdNameType.calendar}},
Items = new NonEmptyArrayOfAllItemsType {Items = new ItemType[] {meeting}}
};
CreateItemResponseType response = ews.CreateItem(request);
var responseMessage = response.ResponseMessages.Items[0];
Microsoft provides an XML example at http://msdn.microsoft.com/en-us/library/aa494190(EXCHG.140).aspx of what the message item should look like. Just setting these properties does not seem to be enough. Can someone tell me what I'm missing or point me to some better examples or documentation?
<CreateItem
xmlns="http://schemas.microsoft.com/exchange/services/2006/messages"
SendMeetingInvitations="SendToAllAndSaveCopy" >
<SavedItemFolderId>
<t:DistinguishedFolderId Id="calendar"/>
</SavedItemFolderId>
<Items>
<t:CalendarItem>
<t:Subject>Meeting with attendee0, attendee1, attendee2</t:Subject>
<t:Body BodyType="Text">CalendarItem:TextBody</t:Body>
<t:Start>2006-06-25T10:00:00Z</t:Start>
<t:End>2006-06-25T11:00:00Z</t:End>
<t:Location>CalendarItem:Location</t:Location>
<t:RequiredAttendees>
<t:Attendee>
<t:Mailbox>
<t:EmailAddress>attendee0#example.com</t:EmailAddress>
</t:Mailbox>
</t:Attendee>
<t:Attendee>
<t:Mailbox>
<t:EmailAddress>attendee1#example.com</t:EmailAddress>
</t:Mailbox>
</t:Attendee>
</t:RequiredAttendees>
<t:OptionalAttendees>
<t:Attendee>
<t:Mailbox>
<t:EmailAddress>attendee2#example.com</t:EmailAddress>
</t:Mailbox>
</t:Attendee>
</t:OptionalAttendees>
<t:Resources>
<t:Attendee>
<t:Mailbox>
<t:EmailAddress>room0#example.com</t:EmailAddress>
</t:Mailbox>
</t:Attendee>
</t:Resources>
</t:CalendarItem>
</Items>
</CreateItem>
This is probably too late for you, but this for anyone else trying this.
The issue seems to be with providing the Is-Specified params. I deleted the IsMeetingSpecified and the request worked. Here's the revised CalendarItemType.
var meeting = new CalendarItemType
{
IsMeeting = true,
Subject = "test EWS",
Body = new BodyType { Value = "test body", BodyType1 = BodyTypeType.HTML },
Start = startDate,
StartSpecified = true,
End = startDate.AddHours(1),
EndSpecified = true,
MeetingTimeZone = new TimeZoneType
{
TimeZoneName = TimeZone.CurrentTimeZone.StandardName,
BaseOffset = "PT0H"
},
Location = "Room 1",
RequiredAttendees = new[] {
new AttendeeType
{
Mailbox =new EmailAddressType
{
EmailAddress ="test#test.com"
}
},
}
};