Mercurial > mm7
comparison MediaPlayer.cpp @ 2315:58be29479e75
add files
author | Ritor1 |
---|---|
date | Wed, 19 Mar 2014 10:09:42 +0600 |
parents | |
children | 9987f93d7e1f |
comparison
equal
deleted
inserted
replaced
2314:8e9be4fa33a8 | 2315:58be29479e75 |
---|---|
1 extern "C" | |
2 { | |
3 #include "lib/libavcodec/avcodec.h" | |
4 #include "lib/libavformat/avformat.h" | |
5 #include "lib/libavutil/avutil.h" | |
6 #include "lib/libavutil/imgutils.h" | |
7 #include "lib/libswscale/swscale.h" | |
8 #include "lib/libswresample/swresample.h" | |
9 #include "lib/libavutil/opt.h" | |
10 //#include "libavutil/samplefmt.h" | |
11 } | |
12 #pragma comment(lib, "avcodec.lib") | |
13 #pragma comment(lib, "avformat.lib") | |
14 #pragma comment(lib, "avutil.lib") | |
15 #pragma comment(lib, "swscale.lib") | |
16 #pragma comment(lib, "swresample.lib") | |
17 | |
18 #include <vector> | |
19 #include <deque> | |
20 | |
21 #include "stuff.h" | |
22 #include "OpenALSoundProvider.h" | |
23 | |
24 #include "MediaPlayer.h" | |
25 using namespace Media; | |
26 | |
27 | |
28 | |
29 | |
30 class MemoryStream | |
31 { | |
32 public: | |
33 inline MemoryStream(void *data, size_t data_size) | |
34 { | |
35 this->data_size = data_size; | |
36 this->data = data; | |
37 this->current_pos = 0; | |
38 } | |
39 inline MemoryStream() | |
40 { | |
41 this->data_size = 0; | |
42 this->data = nullptr; | |
43 this->current_pos = 0; | |
44 } | |
45 | |
46 inline ~MemoryStream() | |
47 { | |
48 if (data) | |
49 delete [] data; | |
50 } | |
51 | |
52 inline size_t Write(void *buffer, size_t num_bytes) | |
53 { | |
54 if (!data) | |
55 { | |
56 data_size = 32 + num_bytes; | |
57 data = new char[data_size]; | |
58 current_pos = 0; | |
59 } | |
60 else if (current_pos + num_bytes >= data_size) | |
61 { | |
62 int new_data_size = data_size + num_bytes + data_size / 8 + 4; | |
63 auto new_data = new char[new_data_size]; | |
64 | |
65 memcpy(new_data, data, data_size); | |
66 delete [] data; | |
67 | |
68 data_size = new_data_size; | |
69 data = new_data; | |
70 } | |
71 memcpy((char *)data + current_pos, buffer, num_bytes); | |
72 current_pos += num_bytes; | |
73 return num_bytes; | |
74 } | |
75 | |
76 inline size_t Read(void *buffer, size_t num_bytes) | |
77 { | |
78 size_t read_size = min(num_bytes, data_size - current_pos); | |
79 if (read_size) | |
80 { | |
81 memcpy(buffer, (char *)data + current_pos, read_size); | |
82 current_pos += read_size; | |
83 } | |
84 return read_size; | |
85 } | |
86 | |
87 inline void Reset() | |
88 { | |
89 current_pos = 0; | |
90 } | |
91 inline void SeekToEnd() | |
92 { | |
93 current_pos = data_size; | |
94 } | |
95 | |
96 inline size_t Unwind(size_t bytes) | |
97 { | |
98 if (bytes > current_pos) | |
99 current_pos = 0; | |
100 else | |
101 current_pos -= bytes; | |
102 return current_pos; | |
103 } | |
104 | |
105 inline size_t Rewind(size_t bytes) | |
106 { | |
107 if (current_pos + bytes >= data_size) | |
108 current_pos = data_size; | |
109 else | |
110 current_pos += bytes; | |
111 return current_pos; | |
112 } | |
113 | |
114 inline size_t Size() const {return data_size;} | |
115 inline size_t Current() const {return current_pos;} | |
116 inline void *Ptr() const {return data;} | |
117 | |
118 private: | |
119 void *data; | |
120 size_t data_size; | |
121 size_t current_pos; | |
122 }; | |
123 | |
124 | |
125 | |
126 | |
127 OpenALSoundProvider *provider = nullptr; | |
128 | |
129 | |
130 | |
131 static int av_num_bytes_per_sample(AVSampleFormat sample_fmt) | |
132 { | |
133 switch (sample_fmt) | |
134 { | |
135 case AV_SAMPLE_FMT_U8: | |
136 case AV_SAMPLE_FMT_U8P: | |
137 return 1; | |
138 | |
139 case AV_SAMPLE_FMT_S16: | |
140 case AV_SAMPLE_FMT_S16P: | |
141 return 2; | |
142 | |
143 case AV_SAMPLE_FMT_S32: | |
144 case AV_SAMPLE_FMT_S32P: | |
145 case AV_SAMPLE_FMT_FLT: | |
146 case AV_SAMPLE_FMT_FLTP: | |
147 return 4; | |
148 | |
149 case AV_SAMPLE_FMT_DBL: | |
150 case AV_SAMPLE_FMT_DBLP: | |
151 return 8; | |
152 | |
153 default: | |
154 case AV_SAMPLE_FMT_NONE: | |
155 Error("Invalid av sample format: %u", sample_fmt); | |
156 } | |
157 return 0; | |
158 } | |
159 | |
160 | |
161 | |
162 struct AVStreamWrapper | |
163 { | |
164 inline AVStreamWrapper() | |
165 { | |
166 this->type = AVMEDIA_TYPE_UNKNOWN; | |
167 this->stream_idx = -1; | |
168 this->stream = nullptr; | |
169 this->dec = nullptr; | |
170 this->dec_ctx = nullptr; | |
171 } | |
172 | |
173 inline void Release() | |
174 { | |
175 type = AVMEDIA_TYPE_UNKNOWN; | |
176 stream_idx = -1; | |
177 stream = nullptr; | |
178 dec = nullptr; | |
179 if (dec_ctx) | |
180 { | |
181 avcodec_close(dec_ctx); | |
182 dec_ctx = nullptr; | |
183 } | |
184 } | |
185 | |
186 AVMediaType type; | |
187 int stream_idx; | |
188 AVStream *stream; | |
189 AVCodec *dec; | |
190 AVCodecContext *dec_ctx; | |
191 }; | |
192 | |
193 struct AVAudioStream: public AVStreamWrapper | |
194 { | |
195 inline AVAudioStream(): | |
196 AVStreamWrapper() | |
197 { | |
198 this->bytes_per_sample = 0; | |
199 this->bytes_per_second = 0; | |
200 } | |
201 | |
202 int bytes_per_sample; | |
203 int bytes_per_second; | |
204 }; | |
205 | |
206 struct AVVideoStream: public AVStreamWrapper | |
207 { | |
208 inline AVVideoStream(): | |
209 AVStreamWrapper() | |
210 { | |
211 this->frames_per_second = 0.0; | |
212 } | |
213 | |
214 double frames_per_second; | |
215 }; | |
216 | |
217 static bool av_open_stream(AVFormatContext *format_ctx, AVMediaType type, AVStreamWrapper *out_stream) | |
218 { | |
219 int stream_idx = av_find_best_stream(format_ctx, type, -1, -1, nullptr, 0); | |
220 if (stream_idx >= 0) | |
221 { | |
222 auto stream = format_ctx->streams[stream_idx]; | |
223 auto dec_ctx = stream->codec; | |
224 auto dec = avcodec_find_decoder(dec_ctx->codec_id); | |
225 if (dec) | |
226 { | |
227 if (avcodec_open2(dec_ctx, dec, nullptr) >= 0) | |
228 { | |
229 out_stream->type = type; | |
230 out_stream->stream_idx = stream_idx; | |
231 out_stream->stream = stream; | |
232 out_stream->dec = dec; | |
233 out_stream->dec_ctx = dec_ctx; | |
234 return true; | |
235 } | |
236 } | |
237 } | |
238 return false; | |
239 } | |
240 | |
241 static bool av_open_audio_stream(AVFormatContext *format_ctx, AVAudioStream *out_stream) | |
242 { | |
243 if (!av_open_stream(format_ctx, AVMEDIA_TYPE_AUDIO, out_stream)) | |
244 return Error("Audio stream not found"), false; | |
245 | |
246 // we support only 2-channel audio for now | |
247 if (out_stream->dec_ctx->channels != 2) | |
248 { | |
249 out_stream->Release(); | |
250 return Error("Unsupported number of channels: %u", out_stream->dec_ctx->channels), false; | |
251 } | |
252 | |
253 out_stream->bytes_per_sample = av_num_bytes_per_sample(out_stream->dec_ctx->sample_fmt); | |
254 out_stream->bytes_per_second = out_stream->dec_ctx->channels * out_stream->dec_ctx->sample_rate * out_stream->bytes_per_sample; | |
255 | |
256 return true; | |
257 } | |
258 | |
259 static bool av_open_video_stream(AVFormatContext *format_ctx, AVVideoStream *out_stream) | |
260 { | |
261 if (!av_open_stream(format_ctx, AVMEDIA_TYPE_VIDEO, out_stream)) | |
262 return Error("Video stream not found"), false; | |
263 | |
264 out_stream->frames_per_second = (double)out_stream->dec_ctx->time_base.den / (double)out_stream->dec_ctx->time_base.num; | |
265 return true; | |
266 } | |
267 | |
268 | |
269 | |
270 void InterleaveAudioData(MemoryStream *stream, AVSampleFormat src_format, int num_channels, int num_samples, uint8_t **channels) | |
271 { | |
272 unsigned int bytes_per_sample; | |
273 switch (src_format) | |
274 { | |
275 case AV_SAMPLE_FMT_U8: | |
276 case AV_SAMPLE_FMT_U8P: | |
277 __debugbreak(); | |
278 | |
279 case AV_SAMPLE_FMT_S16: | |
280 bytes_per_sample = sizeof(__int16); | |
281 stream->Write(channels[0], num_channels * num_samples * bytes_per_sample); | |
282 break; | |
283 | |
284 case AV_SAMPLE_FMT_S16P: | |
285 { | |
286 bytes_per_sample = sizeof(__int16); | |
287 for (int i = 0; i < num_samples; ++i) | |
288 for (int j = 0; j < num_channels; ++j) | |
289 stream->Write(channels[j] + i * bytes_per_sample, bytes_per_sample); | |
290 } | |
291 break; | |
292 | |
293 case AV_SAMPLE_FMT_FLT: | |
294 { | |
295 SwrContext *converter = swr_alloc(); | |
296 av_opt_set_int(converter, "in_channel_layout", av_get_default_channel_layout(2), 0); | |
297 //av_opt_set_int(converter, "in_sample_rate", sample_ra, 0); | |
298 av_opt_set_sample_fmt(converter, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0); | |
299 | |
300 av_opt_set_int(converter, "out_channel_layout", av_get_default_channel_layout(2), 0); | |
301 //av_opt_set_int(converter, "out_sample_rate", dst_sample_rate, 0); | |
302 av_opt_set_sample_fmt(converter, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); | |
303 | |
304 if (swr_init(converter) < 0) | |
305 { | |
306 __debugbreak(); | |
307 swr_free(&converter); | |
308 return; | |
309 } | |
310 | |
311 uint8_t **dst_channels; | |
312 int dst_linesize[8]; | |
313 //int dst_nb_channels = av_get_channel_layout_nb_channels(dst_channel_layout); | |
314 if (av_samples_alloc_array_and_samples(&dst_channels, dst_linesize, 2, num_channels * num_samples, AV_SAMPLE_FMT_S16, 0) < 0) | |
315 { | |
316 __debugbreak(); | |
317 swr_free(&converter); | |
318 return; | |
319 } | |
320 | |
321 if (swr_convert(converter, dst_channels, num_channels * num_samples, (const uint8_t **)channels, num_channels * num_samples) >= 0) | |
322 stream->Write(dst_channels[0], num_channels * num_samples * sizeof(__int16)); | |
323 else | |
324 __debugbreak(); | |
325 | |
326 av_free(dst_channels[0]); | |
327 swr_free(&converter); | |
328 } | |
329 break; | |
330 | |
331 default: | |
332 __debugbreak(); | |
333 //if (Resample(next_frame->avframe, next_frame->avframe->channel_layout, next_frame->avframe->sample_rate, | |
334 // av_get_default_channel_layout(2), next_frame->avframe->sample_rate, AV_SAMPLE_FMT_S16P, resampled_data)) | |
335 } | |
336 } | |
337 | |
338 | |
339 | |
340 bool DecodeAudioFrame(AVCodecContext *dec_ctx, AVPacket *avpacket, AVFrame *avframe, MemoryStream *out_audio_data, int *out_num_audio_samples) | |
341 { | |
342 volatile int decoded = false; | |
343 do | |
344 { | |
345 if (avcodec_decode_audio4(dec_ctx, avframe, (int *)&decoded, avpacket) < 0) | |
346 { | |
347 log("Cannot decode audio frame\n"); | |
348 return false; | |
349 } | |
350 | |
351 if (!decoded) | |
352 log("Cannot decode audio frame in one piece\n"); | |
353 } while (!decoded); | |
354 | |
355 switch (dec_ctx->codec_id) | |
356 { | |
357 case AV_CODEC_ID_BINKAUDIO_RDFT: | |
358 {//pts samples dpts | |
359 // 0 960 | |
360 //17280 960 17280 18x960 | |
361 //18240 960 960 1x960 | |
362 //20160 960 1920 2x960 | |
363 //21120 960 960 1x960 | |
364 //23040 960 1920 2x960 | |
365 static int bink_next_pts = 0; | |
366 | |
367 // there's a gap in the sound - fill empty samples in | |
368 if (bink_next_pts < avpacket->pts) | |
369 { | |
370 short silence[1024]; | |
371 memset(silence, 0, sizeof(silence)); | |
372 | |
373 int samples_to_fill = /*dec_ctx->channels * */(avpacket->pts - bink_next_pts); | |
374 while (samples_to_fill > 0) | |
375 { | |
376 int samples_to_fill_this_step = samples_to_fill >= 1024 ? 1024 : samples_to_fill; | |
377 out_audio_data->Write(silence, samples_to_fill_this_step * sizeof(short)); | |
378 | |
379 samples_to_fill -= samples_to_fill_this_step; | |
380 } | |
381 } | |
382 | |
383 bink_next_pts = avpacket->pts + /*dec_ctx->channels * */avframe->nb_samples; | |
384 } | |
385 break; | |
386 /* | |
387 case AV_CODEC_ID_SMACKAUDIO: | |
388 { | |
389 static int smack_debug_next_audio_time = 0; | |
390 if (smack_debug_next_audio_time != packet->pts) | |
391 { | |
392 Error("There's a gap in the sound before frame %u\n", num_audio_frames); | |
393 __debugbreak(); // there's a gap in the sound | |
394 } | |
395 | |
396 int num_actual_data_channels = 0; | |
397 switch (dec_ctx->sample_fmt) | |
398 { | |
399 case AV_SAMPLE_FMT_U8: | |
400 case AV_SAMPLE_FMT_S16: | |
401 case AV_SAMPLE_FMT_S32: | |
402 case AV_SAMPLE_FMT_FLT: | |
403 case AV_SAMPLE_FMT_DBL: | |
404 num_actual_data_channels = 1; | |
405 break; | |
406 | |
407 case AV_SAMPLE_FMT_U8P: | |
408 case AV_SAMPLE_FMT_S16P: | |
409 case AV_SAMPLE_FMT_S32P: | |
410 case AV_SAMPLE_FMT_FLTP: | |
411 case AV_SAMPLE_FMT_DBLP: | |
412 num_actual_data_channels = dec_ctx->channels; | |
413 break; | |
414 | |
415 default: | |
416 case AV_SAMPLE_FMT_NONE: | |
417 case AV_SAMPLE_FMT_NB: | |
418 __debugbreak(); | |
419 } | |
420 | |
421 smack_debug_next_audio_time += dec_ctx->channels * frame->nb_samples * bytes_per_sample; | |
422 Assert(frame->avframe->linesize[0] == audio.dec_ctx->channels * frame->avframe->nb_samples * audio.bytes_per_sample / num_actual_data_channels, | |
423 "Smack audio size mismatch in frame %u in %s\n", audio_num_read_frames, movie_filename); | |
424 | |
425 frame->play_time = (double)frame->avpacket->pts / (double)audio.bytes_per_second; | |
426 } | |
427 break; | |
428 | |
429 case AV_CODEC_ID_MP3: | |
430 { | |
431 static int mp3_samples_decoded_so_far = 0; | |
432 static int mp3_prev_samples_count = frame->avframe->nb_samples; // mp3 seems to always feed same amount of samples | |
433 frame->play_time = (double)mp3_samples_decoded_so_far / (double)audio.dec_ctx->sample_rate; | |
434 | |
435 mp3_samples_decoded_so_far += frame->avframe->nb_samples; | |
436 Assert(mp3_prev_samples_count == frame->avframe->nb_samples, | |
437 "MP3 audio have variable sample count in frame %u in %s\n", audio_num_read_frames, movie_filename); | |
438 } | |
439 break; | |
440 | |
441 default: | |
442 { | |
443 __debugbreak(); | |
444 double samples_per_second = (double)audio.dec_ctx->time_base.den / (double)audio.dec_ctx->time_base.num; | |
445 double play_length = frame->avframe->nb_samples / samples_per_second; | |
446 frame->play_time = (double)frame->avpacket->pts / samples_per_second; | |
447 } | |
448 break;*/ | |
449 } | |
450 | |
451 if (!avframe->channel_layout) | |
452 { | |
453 log("Audio channel layout not specified, rolling back to default\n"); | |
454 avframe->channel_layout = av_get_default_channel_layout(dec_ctx->channels); | |
455 } | |
456 | |
457 *out_num_audio_samples = dec_ctx->channels * avframe->nb_samples; | |
458 InterleaveAudioData(out_audio_data, dec_ctx->sample_fmt, | |
459 dec_ctx->channels, avframe->nb_samples, avframe->data); | |
460 return true; | |
461 } | |
462 | |
463 | |
464 bool LoadAudioTrack(AVFormatContext *format_ctx, AVCodecContext *dec_ctx, int audio_stream_idx, MemoryStream *out_audio_stream, int *out_num_audio_frames, int *out_num_audio_samples) | |
465 { | |
466 out_audio_stream->Reset(); | |
467 | |
468 AVFrame *frame = avcodec_alloc_frame(); | |
469 AVPacket *packet = new AVPacket; | |
470 av_init_packet(packet); | |
471 | |
472 int num_audio_frames = 0; | |
473 int num_audio_samples = 0; | |
474 while (av_read_frame(format_ctx, packet) >= 0) | |
475 { | |
476 if (packet->stream_index != audio_stream_idx) | |
477 { | |
478 //log("Suspicious stream id %u in %s", packet->stream_index, filenamea); | |
479 continue; | |
480 } | |
481 | |
482 int num_samples_decoded; | |
483 DecodeAudioFrame(dec_ctx, packet, frame, out_audio_stream, &num_samples_decoded); | |
484 | |
485 num_audio_samples += num_samples_decoded; | |
486 num_audio_frames++; | |
487 } | |
488 *out_num_audio_frames = num_audio_frames; | |
489 *out_num_audio_samples = num_audio_samples; | |
490 | |
491 avcodec_free_frame(&frame); | |
492 delete packet; | |
493 | |
494 return true; | |
495 } | |
496 | |
497 | |
498 class Track: public Media::ITrack | |
499 { | |
500 public: | |
501 inline Track() | |
502 { | |
503 this->format_ctx = nullptr; | |
504 this->audio_num_samples = 0; | |
505 } | |
506 | |
507 void Release() | |
508 { | |
509 ReleaseAvcodec(); | |
510 } | |
511 | |
512 void ReleaseAvcodec() | |
513 { | |
514 audio.Release(); | |
515 if (format_ctx) | |
516 { | |
517 av_close_input_file(format_ctx); | |
518 format_ctx = nullptr; | |
519 } | |
520 } | |
521 | |
522 bool Load(const wchar_t *filename) | |
523 { | |
524 char filenamea[1024]; | |
525 sprintf(filenamea, "%S", filename); | |
526 | |
527 if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) | |
528 { | |
529 if (avformat_find_stream_info(format_ctx, nullptr) >= 0) | |
530 { | |
531 av_dump_format(format_ctx, 0, filenamea, 0); | |
532 | |
533 if (!av_open_audio_stream(format_ctx, &audio)) | |
534 { | |
535 Error("Cannot open strack: %s", filenamea); | |
536 return Release(), false; | |
537 } | |
538 | |
539 MemoryStream audio_plain_data; | |
540 int num_audio_frames; | |
541 int num_audio_samples; | |
542 if (LoadAudioTrack(format_ctx, audio.dec_ctx, audio.stream_idx, &audio_plain_data, &num_audio_frames, &num_audio_samples)) | |
543 { | |
544 /*#ifdef _DEBUG | |
545 char debug_filename[1024]; | |
546 sprintf(debug_filename, "%s.wav", filenamea); | |
547 FILE *wav = fopen(debug_filename, "w+b"); | |
548 | |
549 extern void write_wav_header(FILE *wav, int channel_count = 2, int sample_rate = 22050, int bytes_per_sample = 2); | |
550 write_wav_header(wav, audio.dec_ctx->channels, audio.dec_ctx->sample_rate, audio.bytes_per_sample); | |
551 | |
552 fwrite(audio_plain_data.Ptr(), audio_plain_data.Current(), 1, wav); | |
553 | |
554 extern void fix_wav_header(FILE *wav, int wav_bytes_in_stream); | |
555 fix_wav_header(wav, audio_plain_data.Current()); | |
556 #endif*/ | |
557 | |
558 device_buffer = provider->CreateTrack16(audio.dec_ctx->channels, audio.dec_ctx->sample_rate, 2, num_audio_samples, audio_plain_data.Ptr()); | |
559 | |
560 ReleaseAvcodec(); | |
561 return true; | |
562 } | |
563 } | |
564 Release(); | |
565 } | |
566 return false; | |
567 } | |
568 | |
569 virtual void Play(bool loop) | |
570 { | |
571 provider->PlayTrack16(device_buffer, loop); | |
572 } | |
573 | |
574 | |
575 protected: | |
576 AVFormatContext *format_ctx; | |
577 AVAudioStream audio; | |
578 int audio_num_samples; | |
579 | |
580 OpenALSoundProvider::TrackBuffer *device_buffer; | |
581 }; | |
582 | |
583 | |
584 | |
585 class Movie: public Media::IMovie | |
586 { | |
587 public: | |
588 inline Movie() | |
589 { | |
590 this->movie_filename[0] = 0; | |
591 this->width = 0; | |
592 this->height = 0; | |
593 this->format_ctx = nullptr; | |
594 this->end_of_file = false; | |
595 this->playback_time = 0.0; | |
596 | |
597 this->num_audio_frames = 0; | |
598 this->num_audio_samples = 0; | |
599 | |
600 this->last_resampled_frame_num = -1; | |
601 memset(last_resampled_frame_data, 0, sizeof(last_resampled_frame_data)); | |
602 memset(last_resampled_frame_linesize, 0, sizeof(last_resampled_frame_linesize)); | |
603 } | |
604 | |
605 | |
606 inline void Release() | |
607 { | |
608 ReleaseAVCodec(); | |
609 } | |
610 | |
611 inline void ReleaseAVCodec() | |
612 { | |
613 audio.Release(); | |
614 video.Release(); | |
615 if (format_ctx) | |
616 { | |
617 av_close_input_file(format_ctx); | |
618 format_ctx = nullptr; | |
619 } | |
620 } | |
621 | |
622 bool Load(const wchar_t *filename, int dst_width, int dst_height, int cache_ms) | |
623 { | |
624 char filenamea[1024]; | |
625 sprintf(filenamea, "%S", filename); | |
626 sprintf(movie_filename, "%S", filename); | |
627 | |
628 width = dst_width; | |
629 height = dst_height; | |
630 if (avformat_open_input(&format_ctx, filenamea, nullptr, nullptr) >= 0) | |
631 { | |
632 if (avformat_find_stream_info(format_ctx, nullptr) >= 0) | |
633 { | |
634 av_dump_format(format_ctx, 0, filenamea, 0); | |
635 | |
636 if (!av_open_audio_stream(format_ctx, &audio)) | |
637 { | |
638 Error("Cannot open audio stream: %s", filenamea); | |
639 return Release(), false; | |
640 } | |
641 | |
642 if (!av_open_video_stream(format_ctx, &video)) | |
643 { | |
644 Error("Cannot open video stream: %s", filenamea); | |
645 return Release(), false; | |
646 } | |
647 | |
648 decoding_packet = new AVPacket; | |
649 av_init_packet(decoding_packet); | |
650 | |
651 decoding_frame = avcodec_alloc_frame(); | |
652 | |
653 audio_data_in_device = provider->CreateStreamingTrack16(audio.dec_ctx->channels, audio.dec_ctx->sample_rate, 2); | |
654 | |
655 return true; | |
656 } | |
657 } | |
658 return false; | |
659 } | |
660 | |
661 virtual void Play() | |
662 { | |
663 } | |
664 | |
665 virtual void GetNextFrame(double dt, void *dst_surface) | |
666 { | |
667 playback_time += dt; | |
668 | |
669 AVPacket *avpacket = decoding_packet; | |
670 AVFrame *avframe = decoding_frame; | |
671 avcodec_get_frame_defaults(avframe); | |
672 | |
673 int desired_frame_number = floor(playback_time * video.dec_ctx->time_base.den / video.dec_ctx->time_base.num + 0.5); | |
674 if (last_resampled_frame_num == desired_frame_number) | |
675 { | |
676 memcpy(dst_surface, last_resampled_frame_data[0], height * last_resampled_frame_linesize[0]); | |
677 return; | |
678 } | |
679 | |
680 volatile int decoded = false; | |
681 do | |
682 { | |
683 if (av_read_frame(format_ctx, avpacket) < 0) | |
684 { | |
685 // probably movie is finished | |
686 __debugbreak(); | |
687 } | |
688 | |
689 // audio packet - queue into playing | |
690 if (avpacket->stream_index == audio.stream_idx) | |
691 { | |
692 MemoryStream audio_data; | |
693 if (DecodeAudioFrame(audio.dec_ctx, avpacket, avframe, &audio_data, &num_audio_samples)) | |
694 provider->Stream16(audio_data_in_device, num_audio_samples, audio_data.Ptr()); | |
695 } | |
696 // video packet - decode & maybe show | |
697 else if (avpacket->stream_index == video.stream_idx) | |
698 { | |
699 do | |
700 { | |
701 if (avcodec_decode_video2(video.dec_ctx, avframe, (int *)&decoded, avpacket) < 0) | |
702 __debugbreak(); | |
703 } while (!decoded); | |
704 } | |
705 | |
706 } while (avpacket->stream_index != video.stream_idx || | |
707 avpacket->pts != desired_frame_number); | |
708 | |
709 if (decoded) | |
710 { | |
711 if (last_resampled_frame_data[0]) | |
712 av_freep(&last_resampled_frame_data[0]); | |
713 | |
714 AVPixelFormat rescaled_format = AV_PIX_FMT_RGB32; | |
715 uint8_t *rescaled_data[4] = {nullptr, nullptr, nullptr, nullptr}; | |
716 int rescaled_linesize[4] = {0, 0, 0, 0}; | |
717 if (av_image_alloc(rescaled_data, rescaled_linesize, width, height, rescaled_format, 1) >= 0) | |
718 { | |
719 SwsContext *converter = sws_getContext(avframe->width, avframe->height, (AVPixelFormat)avframe->format, | |
720 width, height, rescaled_format, | |
721 SWS_BICUBIC, nullptr, nullptr, nullptr); | |
722 sws_scale(converter, avframe->data, avframe->linesize, 0, avframe->height, rescaled_data, rescaled_linesize); | |
723 sws_freeContext(converter); | |
724 | |
725 memcpy(dst_surface, rescaled_data[0], height * rescaled_linesize[0]); | |
726 | |
727 last_resampled_frame_num = desired_frame_number; | |
728 memcpy(last_resampled_frame_data, rescaled_data, sizeof(rescaled_data)); | |
729 memcpy(last_resampled_frame_linesize, rescaled_linesize, sizeof(rescaled_linesize)); | |
730 } | |
731 } | |
732 else | |
733 memset(dst_surface, 0, width * height * 4); | |
734 } | |
735 | |
736 protected: | |
737 char movie_filename[256]; | |
738 int width; | |
739 int height; | |
740 AVFormatContext *format_ctx; | |
741 double playback_time; | |
742 bool end_of_file; | |
743 | |
744 AVPacket *decoding_packet; | |
745 AVFrame *decoding_frame; | |
746 | |
747 AVAudioStream audio; | |
748 int num_audio_frames; | |
749 int num_audio_samples; | |
750 OpenALSoundProvider::StreamingTrackBuffer *audio_data_in_device; | |
751 | |
752 AVVideoStream video; | |
753 int last_resampled_frame_num; | |
754 uint8_t *last_resampled_frame_data[4]; | |
755 int last_resampled_frame_linesize[4]; | |
756 }; | |
757 | |
758 | |
759 ITrack *Player::LoadTrack(const wchar_t *filename) | |
760 { | |
761 auto track = new Track; | |
762 if (!track->Load(filename)) | |
763 { | |
764 delete track; | |
765 track = nullptr; | |
766 } | |
767 return track; | |
768 } | |
769 | |
770 | |
771 IMovie *Player::LoadMovie(const wchar_t *filename, int width, int height, int cache_ms) | |
772 { | |
773 auto movie = new Movie; | |
774 if (!movie->Load(filename, width, height, cache_ms)) | |
775 { | |
776 delete movie; | |
777 movie = nullptr; | |
778 } | |
779 return movie; | |
780 } | |
781 | |
782 | |
783 | |
784 | |
785 | |
786 | |
787 void av_logger(void *, int, const char *format, va_list args) | |
788 { | |
789 va_list va; | |
790 va_start(va, format); | |
791 char msg[256]; | |
792 vsprintf(msg, format, va); | |
793 va_end(va); | |
794 | |
795 log("av: %s", msg); | |
796 } | |
797 | |
798 Player::Player() | |
799 { | |
800 static int libavcodec_initialized = false; | |
801 | |
802 if (!libavcodec_initialized) | |
803 { | |
804 av_log_set_callback(av_logger); | |
805 avcodec_register_all(); | |
806 av_register_all(); | |
807 | |
808 libavcodec_initialized = true; | |
809 } | |
810 | |
811 if (!provider) | |
812 { | |
813 provider = new OpenALSoundProvider; | |
814 provider->Initialize(); | |
815 } | |
816 } | |
817 | |
818 Player::~Player() | |
819 { | |
820 } |