http://images.24city.com/jimmy/player/default.html
布局思路:
Grid做为最外层容器,分上中下三行
第一行为视频播放窗口,同时单击视频时"暂停"遮罩层也放在这一行,只不过默认不显示而已
第二行为进度条显示区,为了方便布局,在这一行用StackPanel作子容器横向放置了二个控件(进度条和时间显示)
第三行为其它的控制按钮区,也是用StackPanel横向放置其它控件
实现的功能:
1.单击视频,暂停播放,再次单击则继续播放,原则就是利用鼠标单击事件控制Canvas的显示/隐藏以及调用MediaElement的Play(),Pause()方法
2.进度条与播放时间的同步,这里用到了Timer控件,每隔一定时间重新设置进度条的值
3.播放列表采用json字符串解析后绑定实现,同时选择列表的相关视频后,马上播放选择的视频
4.全屏功能
5.静音功能(其实还可以方便左右声道功能,只要不知道界面上怎么放,所以这一块功能没加上去)
6.缓冲以及加载进度的百分比进度显示
7.播放时,预先加载下一段视频(这一块好象效果不明显,主要是对silverlight的缓冲机制不清楚,期待大家共同探讨改进)
另:本示例中用的视频全部为mp4格式的h.264视频
其它不清楚的地方,基本上代码中都有注释
xaml代码:
1
<UserControl x:Class="Test.MainPage"
2
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5
mc:Ignorable="d">
6
<Grid x:Name="LayoutRoot" ShowGridLines="False" >
7
8
<!--Grid布局:分成三行,第一行放视频窗口,第二行为进度条,第三行为其它控制按钮-->
9
<Grid.ColumnDefinitions>
10
<ColumnDefinition Width="*"></ColumnDefinition>
11
</Grid.ColumnDefinitions>
12
13
<Grid.RowDefinitions>
14
<RowDefinition Height="*"></RowDefinition>
15
<RowDefinition Height="22"></RowDefinition>
16
<RowDefinition Height="25"></RowDefinition>
17
</Grid.RowDefinitions>
18
19
20
<!--视频播放控件-->
21
<MediaElement x:Name="media" Source="" Grid.Row="0" Grid.Column="0" CurrentStateChanged="Media_State_Changed" MediaEnded="media_MediaEnded" Cursor="Hand" MouseLeftButtonDown="media_MouseLeftButtonDown" BufferingProgressChanged="media_BufferingProgressChanged" DownloadProgressChanged="media_DownloadProgressChanged"></MediaElement>
22
23
<!--这里用一个Canvas来实现暂停时的遮盖效果-->
24
<Canvas Background="#AAFAEBD7" Grid.Row="0" Grid.Column="0" Cursor="Hand" x:Name="canvas_Pause" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" >
25
<Ellipse Height="200" Width="200" Stroke="AliceBlue" StrokeThickness="10" Canvas.Left="140" Canvas.Top="80"></Ellipse>
26
<Path Stretch="Fill" Stroke="AliceBlue" StrokeThickness="10" Height="98" Width="10" UseLayoutRounding="False" Canvas.Left="203" Canvas.Top="131" Data="M208,136 L208,224"/>
27
<Path Stretch="Fill" Stroke="AliceBlue" StrokeThickness="10" Height="98" Width="10" UseLayoutRounding="False" Canvas.Left="263" Canvas.Top="131" Data="M208,136 L208,224"/>
28
29
<TextBlock Canvas.Left="104" Canvas.Top="296" Foreground="White" >Made by 菩提树下的杨过(http://yjmyzz.cnblogs.com/)</TextBlock>
30
</Canvas>
31
32
<MediaElement x:Name="mediaBuffer" Width="0" Grid.Column="0" Grid.Row="0" BufferingTime="0:0:10" IsMuted="True" AutoPlay="True" ></MediaElement>
33
34
<!--第二行用一个StackPanel横向放了二个子控件,用于显示进度条和当前播放时间-->
35
<StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" >
36
<Slider Height="20" Width="370" x:Name="sliderProgress" ValueChanged="sliderProgress_ValueChanged" Cursor="Hand" ></Slider>
37
<TextBlock Text="00:00:00/00:00:00" Width="110" x:Name="txtTime"/>
38
</StackPanel>
39
40
<!--第三行同样用StackPanel横向放置其它控制按钮-->
41
<StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" >
42
<Button Click="PlayMedia" Content="||" Width="25" Height="25" x:Name="btnPlay" Cursor="Hand" />
43
<Button Click="StopMedia" Content="■" Width="25" Height="25" x:Name="btnStop" Cursor="Hand"/>
44
<TextBlock x:Name="txtProgress" FontSize="12" Width="90" Text="缓冲中
100%" Height="25" TextAlignment="Center" Margin="3,0" Padding="0,6,0,0" ></TextBlock>
45
<ComboBox x:Name="cboList" SelectionChanged="cboList_SelectionChanged" Height="25" Width="232">
46
47
</ComboBox>
48
<Button Content="静" Width="25" Height="25" Margin="3,0" x:Name="btnVoice" Click="btnVoice_Click" Cursor="Hand"></Button>
49
<Slider Height="25" Width="50" x:Name="sliderVoice" Maximum="1" Minimum="0" ValueChanged="sliderVoice_ValueChanged" Value="0.5" Cursor="Hand" ></Slider>
50
<Button Content="全" Width="20" Height="25" Cursor="Hand" x:Name="btnFull" Click="btnFull_Click"></Button>
51
</StackPanel>
52
53
</Grid>
54
</UserControl>
55

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44


45

46

47

48

49

50

51

52

53

54

55

后端cs代码:
1
using System;
2
using System.Collections.Generic;
3
using System.Json;
4
using System.Windows;
5
using System.Windows.Controls;
6
using System.Windows.Input;
7
using System.Windows.Interop;
8
using System.Windows.Threading;
9
10
namespace Test
11
{
12
13
14
public partial class MainPage : UserControl
15
{
16
private DispatcherTimer _timerPlay;
17
18
//实际应用中,以下字符串可通过wcf调用获得
19
private string _medialist = "[{src:'http://task.24city.com/video/01.mp4',name:'苹果王手机第1段'},{src:'http://task.24city.com/video/02.mp4',name:'苹果王手机第2段'},{src:'http://task.24city.com/video/03.mp4',name:'苹果王手机第3段'},{src:'http://task.24city.com/video/04.mp4',name:'蔡依林-柠檬草的味道'},{src:'http://task.24city.com/video/05.mp4',name:'我也不知道是啥'},{src:'http://task.24city.com/video/06.mp4',name:'游戏宣传片段'}]";
20
21
List<MediaItem> _listMedia = null;
22
23
int _currentIndex = -1;//正在播放的元素索引
24
25
26
public MainPage()
27
{
28
InitializeComponent();
29
30
//解析Json
31
JsonValue _jsValue = JsonArray.Parse(_medialist);
32
33
if (_jsValue.Count > 0)
34
{
35
_listMedia = new List<MediaItem>(_jsValue.Count);
36
37
for (int i = 0; i < _jsValue.Count; i++)
38
{
39
_listMedia.Add(new MediaItem() { src = _jsValue[i]["src"], name = _jsValue[i]["name"] });
40
}
41
42
_currentIndex = 0;//默认从第一个开始播放
43
media.Source = new Uri(_listMedia[_currentIndex].src);
44
45
cboList.ItemsSource = _listMedia;
46
cboList.DisplayMemberPath = "name";
47
cboList.SelectedIndex = _currentIndex;
48
49
50
this._timerPlay = new DispatcherTimer();
51
this._timerPlay.Interval = new TimeSpan(0, 0, 0, 0, 100);
52
this._timerPlay.Tick += new EventHandler(timer_Tick);
53
this._timerPlay.Start();
54
}
55
56
57
App.Current.Host.Content.FullScreenChanged += new EventHandler(Content_FullScreenChanged);
58
59
60
}
61
62
/// <summary>
63
/// timer触发时,设置进度条与播放时间同步
64
/// </summary>
65
/// <param name="sender"></param>
66
/// <param name="e"></param>
67
void timer_Tick(object sender, EventArgs e)
68
{
69
this.sliderProgress.Maximum = this.media.NaturalDuration.TimeSpan.TotalSeconds;
70
71
this.sliderProgress.ValueChanged -= new RoutedPropertyChangedEventHandler<double>(sliderProgress_ValueChanged);
72
73
this.sliderProgress.Value = this.media.Position.TotalSeconds;
74
75
this.sliderProgress.ValueChanged += new RoutedPropertyChangedEventHandler<double>(sliderProgress_ValueChanged);
76
77
this.txtTime.Text = media.Position.Hours.ToString().PadLeft(2, '0') + ":" + media.Position.Minutes.ToString().PadLeft(2, '0') + ":" + media.Position.Seconds.ToString().PadLeft(2, '0') + "/" + media.NaturalDuration.TimeSpan.Hours.ToString().PadLeft(2, '0') + ":" + media.NaturalDuration.TimeSpan.Minutes.ToString().PadLeft(2, '0') + ":" + media.NaturalDuration.TimeSpan.Seconds.ToString().PadLeft(2, '0');
78
}
79
80
/// <summary>
81
/// 显示播放状态
82
/// </summary>
83
/// <param name="sender"></param>
84
/// <param name="e"></param>
85
private void Media_State_Changed(object sender, EventArgs e)
86
{
87
88
if (_currentIndex >= 0)
89
{
90
MediaItem _currentMedia = _listMedia[_currentIndex];
91
92
switch (media.CurrentState)
93
{
94
case System.Windows.Media.MediaElementState.AcquiringLicense:
95
txtProgress.Text = "加载许可证
";
96
97
break;
98
case System.Windows.Media.MediaElementState.Buffering:
99
txtProgress.Text = "缓冲中
";
100
break;
101
case System.Windows.Media.MediaElementState.Closed:
102
txtProgress.Text = "已关闭";
103
break;
104
case System.Windows.Media.MediaElementState.Individualizing:
105
txtProgress.Text = "加载个性化设置
";
106
break;
107
case System.Windows.Media.MediaElementState.Opening:
108
txtProgress.Text = "加载中
";
109
break;
110
case System.Windows.Media.MediaElementState.Paused:
111
txtProgress.Text = "已暂停";
112
break;
113
case System.Windows.Media.MediaElementState.Playing:
114
txtProgress.Text = "正在播放";
115
116
//预选缓冲下一段视频(不过在实际测试中,感觉这么干没啥明显效果,欢迎大家共同探讨如何提前缓冲下一段视频)
117
if (_currentIndex + 1 > _listMedia.Count - 1)
118
{
119
mediaBuffer.Source = new Uri(_listMedia[0].src);
120
}
121
else
122
{
123
mediaBuffer.Source = new Uri(_listMedia[_currentIndex + 1].src);
124
}
125
126
break;
127
case System.Windows.Media.MediaElementState.Stopped:
128
txtProgress.Text = "已停止";
129
break;
130
default:
131
break;
132
}
133
}
134
135
}
136
137
138
/// <summary>
139
/// 停止播放
140
/// </summary>
141
/// <param name="sender"></param>
142
/// <param name="e"></param>
143
private void StopMedia(object sender, System.Windows.RoutedEventArgs e)
144
{
145
media.Stop();
146
btnPlay.Content = ">";
147
148
}
149
150
151
152
/// <summary>
153
/// 播放/暂停
154
/// </summary>
155
/// <param name="sender"></param>
156
/// <param name="e"></param>
157
private void PlayMedia(object sender, System.Windows.RoutedEventArgs e)
158
{
159
160
if (media.CurrentState == System.Windows.Media.MediaElementState.Paused || media.CurrentState == System.Windows.Media.MediaElementState.Stopped)
161
{
162
media.Play();
163
btnPlay.Content = "||";
164
canvas_Pause.Visibility = Visibility.Collapsed;
165
166
}
167
else
168
{
169
media.Pause();
170
btnPlay.Content = ">";
171
canvas_Pause.Visibility = Visibility.Visible;
172
}
173
174
}
175
176
/// <summary>
177
/// 一段播放完毕后,自动跳到下一段
178
/// </summary>
179
/// <param name="sender"></param>
180
/// <param name="e"></param>
181
private void media_MediaEnded(object sender, System.Windows.RoutedEventArgs e)
182
{
183
_currentIndex++;
184
185
if (_currentIndex > _listMedia.Count - 1)
186
{
187
_currentIndex = 0;
188
}
189
190
media.Source = new Uri(_listMedia[_currentIndex].src);
191
192
cboList.SelectedIndex = _currentIndex;
193
}
194
195
/// <summary>
196
/// 下拉列表改变时,播放相关片段
197
/// </summary>
198
/// <param name="sender"></param>
199
/// <param name="e"></param>
200
private void cboList_SelectionChanged(object sender, SelectionChangedEventArgs e)
201
{
202
_currentIndex = cboList.SelectedIndex;
203
if (_currentIndex > _listMedia.Count - 1)
204
{
205
_currentIndex = 0;
206
}
207
208
media.Source = new Uri(_listMedia[_currentIndex].src);
209
canvas_Pause.Visibility = System.Windows.Visibility.Collapsed;
210
media.Position = new TimeSpan(0,0,0,0,0);
211
sliderProgress.Value = 0;
212
}
213
214
/// <summary>
215
/// 暂时Canvas点击后,隐藏Canvas,同时继续播放
216
/// </summary>
217
/// <param name="sender"></param>
218
/// <param name="e"></param>
219
private void Canvas_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
220
{
221
canvas_Pause.Visibility = System.Windows.Visibility.Collapsed;
222
media.Play();
223
}
224
225
/// <summary>
226
/// 视频画面单击时,暂时播放
227
/// </summary>
228
/// <param name="sender"></param>
229
/// <param name="e"></param>
230
private void media_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
231
{
232
canvas_Pause.Visibility = System.Windows.Visibility.Visible;
233
media.Pause();
234
}
235
236
/// <summary>
237
/// 进度条拖动时,跳到相关位置
238
/// </summary>
239
/// <param name="sender"></param>
240
/// <param name="e"></param>
241
private void sliderProgress_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<double> e)
242
{
243
this.media.Position = new TimeSpan((long)(e.NewValue * 1000 * 1000 * 10));
244
}
245
246
247
//private void media_BufferingProgressChanged(object sender, RoutedEventHandler e)
248
//{
249
// txtTime.Text = "缓冲中
" + media.BufferingProgress.ToString("F0") + "%";
250
//}
251
252
/// <summary>
253
/// 静音按钮的实现
254
/// </summary>
255
/// <param name="sender"></param>
256
/// <param name="e"></param>
257
private void btnVoice_Click(object sender, RoutedEventArgs e)
258
{
259
if (media.IsMuted)
260
{
261
media.IsMuted = false;
262
this.btnVoice.Content = "静";
263
sliderVoice.IsEnabled = true;
264
}
265
else
266
{
267
media.IsMuted = true;
268
this.btnVoice.Content = "音";
269
sliderVoice.IsEnabled = false;
270
}
271
272
}
273
274
/// <summary>
275
/// 调整音量大小
276
/// </summary>
277
/// <param name="sender"></param>
278
/// <param name="e"></param>
279
private void sliderVoice_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
280
{
281
if (sliderVoice == null)
282
{
283
return;
284
}
285
media.Volume = sliderVoice.Value;
286
}
287
288
private void btnFull_Click(object sender, RoutedEventArgs e)
289
{
290
Content contentObj = App.Current.Host.Content;
291
contentObj.IsFullScreen = !contentObj.IsFullScreen;
292
293
294
}
295
296
297
298
private void Content_FullScreenChanged(object sender, EventArgs e)
299
{
300
Content contentObj = App.Current.Host.Content;
301
302
if (contentObj.IsFullScreen)
303
{
304
btnFull.Content = "退";
305
}
306
else
307
{
308
btnFull.Content = "全";
309
}
310
}
311
312
/// <summary>
313
/// 显示缓冲进度
314
/// </summary>
315
/// <param name="sender"></param>
316
/// <param name="e"></param>
317
private void media_BufferingProgressChanged(object sender, RoutedEventArgs e)
318
{
319
this.txtProgress.Text = "缓冲中
" + (media.BufferingProgress*100).ToString("F0") + "%";
320
canvas_Pause.Visibility = Visibility.Visible;
321
if (media.BufferingProgress >= 1.0)
322
{
323
canvas_Pause.Visibility = Visibility.Collapsed;
324
switch (media.CurrentState)
325
{
326
case System.Windows.Media.MediaElementState.Buffering:
327
txtProgress.Text = "缓冲中
";
328
break;
329
case System.Windows.Media.MediaElementState.Closed:
330
txtProgress.Text = "已关闭";
331
break;
332
case System.Windows.Media.MediaElementState.Paused:
333
txtProgress.Text = "已暂停";
334
break;
335
case System.Windows.Media.MediaElementState.Playing:
336
txtProgress.Text = "正在播放";
337
break;
338
case System.Windows.Media.MediaElementState.Stopped:
339
txtProgress.Text = "已停止";
340
break;
341
default:
342
txtProgress.Text = "就绪";
343
break;
344
}
345
}
346
}
347
348
/// <summary>
349
/// 显示加载进度
350
/// </summary>
351
/// <param name="sender"></param>
352
/// <param name="e"></param>
353
private void media_DownloadProgressChanged(object sender, RoutedEventArgs e)
354
{
355
txtProgress.Text = "加载中
" + (media.DownloadProgress * 100).ToString("F0") + "%";
356
if (media.DownloadProgress >= 1)
357
{
358
switch (media.CurrentState)
359
{
360
case System.Windows.Media.MediaElementState.Buffering:
361
txtProgress.Text = "缓冲中
";
362
break;
363
case System.Windows.Media.MediaElementState.Closed:
364
txtProgress.Text = "已关闭";
365
break;
366
case System.Windows.Media.MediaElementState.Paused:
367
txtProgress.Text = "已暂停";
368
break;
369
case System.Windows.Media.MediaElementState.Playing:
370
txtProgress.Text = "正在播放";
371
break;
372
case System.Windows.Media.MediaElementState.Stopped:
373
txtProgress.Text = "已停止";
374
break;
375
default:
376
txtProgress.Text = "就绪";
377
break;
378
}
379
}
380
}
381
382
383
384
385
}
386
387
388
/// <summary>
389
/// 媒体元素Item自定义类
390
/// </summary>
391
public class MediaItem
392
{
393
public string src { set; get; }
394
public string name { set; get; }
395
}
396
}
397

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95


96

97

98

99


100

101

102

103

104

105


106

107

108


109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249


250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319


320

321

322

323

324

325

326

327


328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355


356

357

358

359

360

361


362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397
