root/trunk/showtime/src/media.c @ 4005

Revision 4005, 19.5 KB (checked in by andoma, 8 months ago)

Add support for ejecting media

Line 
1/*
2 *  Media streaming functions and ffmpeg wrappers
3 *  Copyright (C) 2007 Andreas Öman
4 *
5 *  This program is free software: you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation, either version 3 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <assert.h>
20#include <unistd.h>
21#include <errno.h>
22#include <sys/time.h>
23#include <time.h>
24#include <string.h>
25#include "media.h"
26#include "showtime.h"
27#include "audio/audio_decoder.h"
28#include "event.h"
29#include "playqueue.h"
30#include "fileaccess/fileaccess.h"
31
32static hts_mutex_t media_mutex;
33
34static prop_t *media_prop_root;
35static prop_t *media_prop_sources;
36static prop_t *media_prop_current;
37
38static struct media_pipe_list media_pipe_stack;
39media_pipe_t *media_primary;
40
41static void seek_by_propchange(void *opaque, prop_event_t event, ...);
42
43static void update_avdelta(void *opaque, prop_event_t event, ...);
44
45static void media_eventsink(void *opaque, prop_event_t event, ...);
46
47
48/**
49 *
50 */
51void
52media_init(void)
53{
54  hts_mutex_init(&media_mutex);
55
56  LIST_INIT(&media_pipe_stack);
57
58  media_prop_root    = prop_create(prop_get_global(), "media");
59  media_prop_sources = prop_create(media_prop_root, "sources");
60  media_prop_current = prop_create(media_prop_root, "current");
61
62  prop_subscribe(0,
63                 PROP_TAG_NAME("media", "eventsink"),
64                 PROP_TAG_CALLBACK, media_eventsink, NULL,
65                 PROP_TAG_MUTEX, &media_mutex,
66                 PROP_TAG_ROOT, media_prop_root,
67                 NULL);
68}
69
70
71/**
72 *
73 */
74void
75media_buf_free(media_buf_t *mb)
76{
77  if(mb->mb_data != NULL)
78    free(mb->mb_data);
79
80  if(mb->mb_cw != NULL)
81    wrap_codec_deref(mb->mb_cw);
82 
83  free(mb);
84}
85
86
87
88/**
89 *
90 */
91static void
92mq_init(media_queue_t *mq, prop_t *p)
93{
94  TAILQ_INIT(&mq->mq_q);
95  mq->mq_len = 0;
96  mq->mq_stream = -1;
97  hts_cond_init(&mq->mq_avail);
98  mq->mq_prop_qlen_cur = prop_create(p, "qlen");
99  mq->mq_prop_qlen_max = prop_create(p, "qmax");
100}
101
102
103/**
104 *
105 */
106static void
107mq_destroy(media_queue_t *mq)
108{
109  hts_cond_destroy(&mq->mq_avail);
110}
111
112
113/**
114 *
115 */
116media_pipe_t *
117mp_create(const char *name, const char *type, int flags)
118{
119  media_pipe_t *mp;
120
121  mp = calloc(1, sizeof(media_pipe_t));
122  mp->mp_flags = flags;
123
124  TAILQ_INIT(&mp->mp_eq);
125
126  mp->mp_refcount = 1;
127
128  mp->mp_name = name;
129
130  hts_mutex_init(&mp->mp_mutex);
131  hts_mutex_init(&mp->mp_clock_mutex);
132  hts_cond_init(&mp->mp_backpressure);
133 
134  mp->mp_prop_root = prop_create(media_prop_sources, NULL);
135
136  if(type != NULL) {
137    mp->mp_flags |= MP_PRIMABLE;
138    prop_set_string(prop_create(mp->mp_prop_root, "type"), type);
139  }
140
141  mq_init(&mp->mp_audio, prop_create(mp->mp_prop_root, "audio"));
142  mq_init(&mp->mp_video, prop_create(mp->mp_prop_root, "video"));
143
144  mp->mp_prop_metadata    = prop_create(mp->mp_prop_root, "metadata");
145  mp->mp_prop_playstatus  = prop_create(mp->mp_prop_root, "playstatus");
146  mp->mp_prop_currenttime = prop_create(mp->mp_prop_root, "currenttime");
147  mp->mp_prop_avdelta     = prop_create(mp->mp_prop_root, "avdelta");
148  prop_set_float(mp->mp_prop_avdelta, 0);
149
150  mp->mp_prop_url         = prop_create(mp->mp_prop_root, "url");
151
152  mp->mp_prop_canSkipBackward =
153    prop_create(mp->mp_prop_root, "canSkipBackward");
154
155  mp->mp_prop_canSkipForward =
156    prop_create(mp->mp_prop_root, "canSkipForward");
157
158  mp->mp_prop_canSeek =
159    prop_create(mp->mp_prop_root, "canSeek");
160
161  mp->mp_prop_canPause =
162    prop_create(mp->mp_prop_root, "canPause");
163
164  mp->mp_prop_canEject =
165    prop_create(mp->mp_prop_root, "canEject");
166
167  mp->mp_pc = prop_courier_create(&mp->mp_mutex, PROP_COURIER_THREAD, "mp");
168
169  mp->mp_sub_currenttime =
170    prop_subscribe(PROP_SUB_NO_INITIAL_UPDATE,
171                   PROP_TAG_CALLBACK, seek_by_propchange, mp,
172                   PROP_TAG_COURIER, mp->mp_pc,
173                   PROP_TAG_ROOT, mp->mp_prop_currenttime,
174                   NULL);
175
176  mp->mp_sub_avdelta =
177    prop_subscribe(PROP_SUB_NO_INITIAL_UPDATE,
178                   PROP_TAG_CALLBACK, update_avdelta, mp,
179                   PROP_TAG_COURIER, mp->mp_pc,
180                   PROP_TAG_ROOT, mp->mp_prop_avdelta,
181                   NULL);
182  return mp;
183}
184
185
186/**
187 *
188 */
189static void
190mp_destroy(media_pipe_t *mp)
191{
192  event_t *e;
193
194  /* Make sure a clean shutdown has been made */
195  assert(mp->mp_audio_decoder == NULL);
196  assert(mp != media_primary);
197  assert(!(mp->mp_flags & MP_ON_STACK));
198
199  prop_unsubscribe(mp->mp_sub_currenttime);
200  prop_unsubscribe(mp->mp_sub_avdelta);
201
202  prop_courier_destroy(mp->mp_pc);
203
204  while((e = TAILQ_FIRST(&mp->mp_eq)) != NULL) {
205    TAILQ_REMOVE(&mp->mp_eq, e, e_link);
206    event_unref(e);
207  }
208
209  mq_destroy(&mp->mp_audio);
210  mq_destroy(&mp->mp_video);
211
212  prop_destroy(mp->mp_prop_root);
213
214  hts_cond_destroy(&mp->mp_backpressure);
215  hts_mutex_destroy(&mp->mp_mutex);
216  hts_mutex_destroy(&mp->mp_clock_mutex);
217
218  free(mp);
219}
220
221
222/**
223 *
224 */
225void
226mp_ref_dec(media_pipe_t *mp)
227{
228  if(atomic_add(&mp->mp_refcount, -1) == 1)
229    mp_destroy(mp);
230}
231
232
233/**
234 *
235 */
236static void
237mp_enqueue_event_locked(media_pipe_t *mp, event_t *e)
238{
239  atomic_add(&e->e_refcount, 1);
240  TAILQ_INSERT_TAIL(&mp->mp_eq, e, e_link);
241  hts_cond_signal(&mp->mp_backpressure);
242}
243
244/**
245 *
246 */
247void
248mp_enqueue_event(media_pipe_t *mp, event_t *e)
249{
250  hts_mutex_lock(&mp->mp_mutex);
251  mp_enqueue_event_locked(mp, e);
252  hts_mutex_unlock(&mp->mp_mutex);
253}
254
255
256/**
257 *
258 */
259event_t *
260mp_dequeue_event(media_pipe_t *mp)
261{
262  event_t *e;
263  hts_mutex_lock(&mp->mp_mutex);
264
265  while((e = TAILQ_FIRST(&mp->mp_eq)) == NULL)
266    hts_cond_wait(&mp->mp_backpressure, &mp->mp_mutex);
267
268  TAILQ_REMOVE(&mp->mp_eq, e, e_link);
269  hts_mutex_unlock(&mp->mp_mutex);
270  return e;
271}
272
273
274/**
275 *
276 */
277event_t *
278mp_dequeue_event_deadline(media_pipe_t *mp, int timeout)
279{
280  event_t *e;
281
282  hts_mutex_lock(&mp->mp_mutex);
283
284  while((e = TAILQ_FIRST(&mp->mp_eq)) == NULL) {
285    if(hts_cond_wait_timeout(&mp->mp_backpressure, &mp->mp_mutex, timeout))
286      break;
287  }
288  if(e != NULL)
289    TAILQ_REMOVE(&mp->mp_eq, e, e_link);
290
291  hts_mutex_unlock(&mp->mp_mutex);
292  return e;
293}
294
295
296/**
297 *
298 */
299event_t *
300mp_wait_for_empty_queues(media_pipe_t *mp, int limit)
301{
302  event_t *e;
303  hts_mutex_lock(&mp->mp_mutex);
304
305  while((e = TAILQ_FIRST(&mp->mp_eq)) == NULL &&
306        (mp->mp_audio.mq_len > limit || mp->mp_video.mq_len > limit))
307    hts_cond_wait(&mp->mp_backpressure, &mp->mp_mutex);
308
309  if(e != NULL)
310    TAILQ_REMOVE(&mp->mp_eq, e, e_link);
311  hts_mutex_unlock(&mp->mp_mutex);
312  return e;
313}
314
315
316
317/**
318 *
319 */
320static void
321mb_enq_tail(media_queue_t *mq, media_buf_t *mb)
322{
323  TAILQ_INSERT_TAIL(&mq->mq_q, mb, mb_link);
324  mq->mq_len++;
325  hts_cond_signal(&mq->mq_avail);
326  prop_set_int(mq->mq_prop_qlen_cur, mq->mq_len);
327}
328
329/**
330 *
331 */
332static void
333mb_enq_head(media_queue_t *mq, media_buf_t *mb)
334{
335  TAILQ_INSERT_HEAD(&mq->mq_q, mb, mb_link);
336  mq->mq_len++;
337  hts_cond_signal(&mq->mq_avail);
338  prop_set_int(mq->mq_prop_qlen_cur, mq->mq_len);
339}
340
341/**
342 *
343 */
344event_t *
345mb_enqueue_with_events(media_pipe_t *mp, media_queue_t *mq, media_buf_t *mb)
346{
347  media_queue_t *v = &mp->mp_video;
348  media_queue_t *a = &mp->mp_audio;
349  event_t *e = NULL;
350
351  hts_mutex_lock(&mp->mp_mutex);
352
353  if(a->mq_stream >= 0 && v->mq_stream >= 0) {
354    while((e = TAILQ_FIRST(&mp->mp_eq)) == NULL &&
355          ((a->mq_len > MQ_LOWWATER && v->mq_len > MQ_LOWWATER) ||
356           a->mq_len > MQ_HIWATER || v->mq_len > MQ_HIWATER)) {
357      hts_cond_wait(&mp->mp_backpressure, &mp->mp_mutex);
358    }
359  } else {
360    while((e = TAILQ_FIRST(&mp->mp_eq)) == NULL && mq->mq_len > MQ_LOWWATER)
361      hts_cond_wait(&mp->mp_backpressure, &mp->mp_mutex);
362  }
363
364  if(e != NULL) {
365    TAILQ_REMOVE(&mp->mp_eq, e, e_link);
366    hts_mutex_unlock(&mp->mp_mutex);
367    return e;
368  }
369
370  mb_enq_tail(mq, mb);
371  hts_mutex_unlock(&mp->mp_mutex);
372  return NULL;
373}
374
375
376
377/**
378 * Return -1 if queues are full. return 0 if enqueue succeeded.
379 */
380int
381mb_enqueue_no_block(media_pipe_t *mp, media_queue_t *mq, media_buf_t *mb)
382{
383  media_queue_t *v = &mp->mp_video;
384  media_queue_t *a = &mp->mp_audio;
385
386  hts_mutex_lock(&mp->mp_mutex);
387 
388  if(a->mq_stream >= 0 && v->mq_stream >= 0) {
389
390    if((a->mq_len > MQ_LOWWATER && v->mq_len > MQ_LOWWATER) ||
391       a->mq_len > MQ_HIWATER || v->mq_len > MQ_HIWATER) {
392      hts_mutex_unlock(&mp->mp_mutex);
393      return -1;
394    }
395
396  } else {
397
398    if(mq->mq_len > MQ_LOWWATER) {
399      hts_mutex_unlock(&mp->mp_mutex);
400      return -1;
401    }
402  }
403 
404  mb_enq_tail(mq, mb);
405  hts_mutex_unlock(&mp->mp_mutex);
406  return 0;
407}
408
409
410/**
411 *
412 */
413void
414mb_enqueue_always(media_pipe_t *mp, media_queue_t *mq, media_buf_t *mb)
415{
416  hts_mutex_lock(&mp->mp_mutex);
417  mb_enq_tail(mq, mb);
418  hts_mutex_unlock(&mp->mp_mutex);
419}
420
421
422/*
423 * Must be called with mp locked
424 */
425
426void
427mq_flush(media_queue_t *mq)
428{
429  media_buf_t *mb;
430
431  while((mb = TAILQ_FIRST(&mq->mq_q)) != NULL) {
432    TAILQ_REMOVE(&mq->mq_q, mb, mb_link);
433    media_buf_free(mb);
434  }
435  mq->mq_len = 0;
436}
437
438
439/*
440 *
441 */
442
443void
444mp_flush(media_pipe_t *mp)
445{
446  media_queue_t *v = &mp->mp_video;
447  media_queue_t *a = &mp->mp_audio;
448  media_buf_t *mb;
449
450  hts_mutex_lock(&mp->mp_mutex);
451
452  mq_flush(a);
453  mq_flush(v);
454
455  if(v->mq_stream >= 0) {
456    mb = media_buf_alloc();
457    mb->mb_data_type = MB_FLUSH;
458    mb_enq_tail(v, mb);
459  }
460
461  if(a->mq_stream >= 0) {
462    mb = media_buf_alloc();
463    mb->mb_data_type = MB_FLUSH;
464    mb_enq_tail(a, mb);
465  }
466  hts_mutex_unlock(&mp->mp_mutex);
467
468}
469
470/**
471 *
472 */
473void
474mp_end(media_pipe_t *mp)
475{
476  media_queue_t *v = &mp->mp_video;
477  media_queue_t *a = &mp->mp_audio;
478  media_buf_t *mb;
479
480  hts_mutex_lock(&mp->mp_mutex);
481
482  if(v->mq_stream >= 0) {
483    mb = media_buf_alloc();
484    mb->mb_data_type = MB_END;
485    mb_enq_tail(v, mb);
486  }
487
488  if(a->mq_stream >= 0) {
489    mb = media_buf_alloc();
490    mb->mb_data_type = MB_END;
491    mb_enq_tail(a, mb);
492  }
493  hts_mutex_unlock(&mp->mp_mutex);
494
495}
496
497
498/*
499 *
500 */
501
502void
503mp_wait(media_pipe_t *mp, int audio, int video)
504{
505  while(1) {
506    usleep(100000);
507    if(audio && mp->mp_audio.mq_len > 0)
508      continue;
509
510    if(video && mp->mp_video.mq_len > 0)
511      continue;
512    break;
513  }
514}
515
516/*
517 *
518 */
519
520void
521mp_send_cmd(media_pipe_t *mp, media_queue_t *mq, int cmd)
522{
523  media_buf_t *mb;
524
525  hts_mutex_lock(&mp->mp_mutex);
526
527  mb = media_buf_alloc();
528  mb->mb_cw = NULL;
529  mb->mb_data = NULL;
530  mb->mb_data_type = cmd;
531  mb_enq_tail(mq, mb);
532  hts_mutex_unlock(&mp->mp_mutex);
533}
534
535/*
536 *
537 */
538void
539mp_send_cmd_head(media_pipe_t *mp, media_queue_t *mq, int cmd)
540{
541  media_buf_t *mb;
542
543  hts_mutex_lock(&mp->mp_mutex);
544
545  mb = media_buf_alloc();
546  mb->mb_cw = NULL;
547  mb->mb_data = NULL;
548  mb->mb_data_type = cmd;
549  mb_enq_head(mq, mb);
550  hts_mutex_unlock(&mp->mp_mutex);
551}
552
553/*
554 *
555 */
556
557void
558mp_send_cmd_data(media_pipe_t *mp, media_queue_t *mq, int cmd, void *d)
559{
560 media_buf_t *mb;
561
562  hts_mutex_lock(&mp->mp_mutex);
563
564  mb = media_buf_alloc();
565  mb->mb_cw = NULL;
566  mb->mb_data_type = cmd;
567  mb->mb_data = d;
568  mb_enq_tail(mq, mb);
569  hts_mutex_unlock(&mp->mp_mutex);
570}
571
572/*
573 *
574 */
575
576void
577mp_send_cmd_u32_head(media_pipe_t *mp, media_queue_t *mq, int cmd, uint32_t u)
578{
579  media_buf_t *mb;
580
581  hts_mutex_lock(&mp->mp_mutex);
582
583  mb = media_buf_alloc();
584  mb->mb_cw = NULL;
585  mb->mb_data_type = cmd;
586  mb->mb_data = NULL;
587  mb->mb_data32 = u;
588  mb_enq_head(mq, mb);
589  hts_mutex_unlock(&mp->mp_mutex);
590}
591
592/*
593 *
594 */
595
596void
597mp_send_cmd_u32(media_pipe_t *mp, media_queue_t *mq, int cmd, uint32_t u)
598{
599  media_buf_t *mb;
600
601  hts_mutex_lock(&mp->mp_mutex);
602
603  mb = media_buf_alloc();
604  mb->mb_cw = NULL;
605  mb->mb_data_type = cmd;
606  mb->mb_data = NULL;
607  mb->mb_data32 = u;
608  mb_enq_tail(mq, mb);
609  hts_mutex_unlock(&mp->mp_mutex);
610}
611
612
613
614/**
615 *
616 */
617codecwrap_t *
618wrap_codec_ref(codecwrap_t *cw)
619{
620  atomic_add(&cw->refcount, 1);
621  return cw;
622}
623
624/**
625 *
626 */
627void
628wrap_codec_deref(codecwrap_t *cw)
629{
630  formatwrap_t *fw = cw->fw;
631
632  if(atomic_add(&cw->refcount, -1) > 1)
633    return;
634
635  avcodec_close(cw->codec_ctx);
636
637  if(fw == NULL)
638    free(cw->codec_ctx);
639
640  if(cw->parser_ctx != NULL)
641    av_parser_close(cw->parser_ctx);
642 
643  if(fw != NULL)
644    wrap_format_deref(fw);
645
646  free(cw);
647}
648
649
650/**
651 *
652 */
653codecwrap_t *
654wrap_codec_create(enum CodecID id, enum CodecType type, int parser,
655                  formatwrap_t *fw, AVCodecContext *ctx,
656                  int cheat_for_speed, int sub_id)
657{
658  extern int concurrency;
659  codecwrap_t *cw = malloc(sizeof(codecwrap_t));
660
661  cw->codec = avcodec_find_decoder(id);
662  if(cw->codec == NULL) {
663    free(cw);
664    return NULL;
665  }
666 
667  cw->codec_ctx = ctx ?: avcodec_alloc_context();
668
669  cw->codec_ctx->codec_id   = cw->codec->id;
670  cw->codec_ctx->codec_type = cw->codec->type;
671  cw->codec_ctx->sub_id = sub_id;
672
673  if(avcodec_open(cw->codec_ctx, cw->codec) < 0) {
674    if(ctx == NULL)
675      free(cw->codec_ctx);
676    free(cw);
677    return NULL;
678  }
679
680  cw->parser_ctx = parser ? av_parser_init(id) : NULL;
681
682  cw->refcount = 1;
683  cw->fw = fw;
684 
685  if(fw != NULL)
686    atomic_add(&fw->refcount, 1);
687
688  if(type == CODEC_TYPE_VIDEO && concurrency > 1) {
689    avcodec_thread_init(cw->codec_ctx, concurrency);
690   
691    if(cheat_for_speed)
692      cw->codec_ctx->flags2 |= CODEC_FLAG2_FAST;
693  }
694
695  return cw;
696}
697
698/**
699 *
700 */
701formatwrap_t *
702wrap_format_create(AVFormatContext *fctx)
703{
704  formatwrap_t *fw = malloc(sizeof(formatwrap_t));
705  fw->refcount = 1;
706  fw->fctx = fctx;
707  return fw;
708}
709
710
711/**
712 *
713 */
714void
715wrap_format_deref(formatwrap_t *fw)
716{
717  if(atomic_add(&fw->refcount, -1) > 1)
718    return;
719  av_close_input_file(fw->fctx);
720  free(fw);
721}
722
723
724/**
725 *
726 */
727static void
728mp_set_primary(media_pipe_t *mp)
729{
730  media_primary = mp;
731  event_t *e = event_create_type(EVENT_MP_IS_PRIMARY);
732  mp_enqueue_event(mp, e);
733  event_unref(e);
734
735  prop_select(mp->mp_prop_root, 0);
736  prop_link(mp->mp_prop_root, media_prop_current);
737}
738
739
740/**
741 *
742 */
743void
744mp_become_primary(struct media_pipe *mp)
745{
746  if(mp->mp_audio_decoder == NULL)
747    mp->mp_audio_decoder = audio_decoder_create(mp);
748   
749  if(media_primary == mp)
750    return;
751
752  hts_mutex_lock(&media_mutex);
753
754  assert(mp->mp_flags & MP_PRIMABLE);
755
756  if(media_primary != NULL) {
757   
758    LIST_INSERT_HEAD(&media_pipe_stack, media_primary, mp_stack_link);
759    media_primary->mp_flags |= MP_ON_STACK;
760
761    mp_enqueue_event(media_primary,
762                     event_create_type(EVENT_MP_NO_LONGER_PRIMARY));
763  }
764
765  mp_ref_inc(mp);
766  mp_set_primary(mp);
767
768  hts_mutex_unlock(&media_mutex);
769}
770
771
772/**
773 *
774 */
775void
776mp_shutdown(struct media_pipe *mp)
777{
778  if(mp->mp_audio_decoder != NULL) {
779    audio_decoder_destroy(mp->mp_audio_decoder);
780    mp->mp_audio_decoder = NULL;
781  }
782
783  hts_mutex_lock(&media_mutex);
784
785  assert(mp->mp_flags & MP_PRIMABLE);
786
787  if(media_primary == mp) {
788    /* We were primary */
789
790    prop_unlink(media_prop_current);
791
792    media_primary = NULL;
793    mp_ref_dec(mp); // mp could be free'd here */
794
795    /* Anyone waiting to regain playback focus? */
796    if((mp = LIST_FIRST(&media_pipe_stack)) != NULL) {
797
798      assert(mp->mp_flags & MP_ON_STACK);
799      LIST_REMOVE(mp, mp_stack_link);
800      mp->mp_flags &= ~MP_ON_STACK;
801      mp_set_primary(mp);
802    } else {
803      prop_unselect(media_prop_sources);
804    }
805
806
807  } else if(mp->mp_flags & MP_ON_STACK) {
808    // We are on the stack
809
810    LIST_REMOVE(mp, mp_stack_link);
811    mp->mp_flags &= ~MP_ON_STACK;
812
813    mp_ref_dec(mp); // mp could be free'd here */
814  }
815  hts_mutex_unlock(&media_mutex);
816}
817
818
819
820
821
822void
823nice_codec_name(char *buf, int len, AVCodecContext *ctx)
824{
825  const char *fill = NULL;
826
827  switch(ctx->codec_id) {
828  case CODEC_ID_AC3:
829    fill = "ac3";
830    break;
831
832  case CODEC_ID_MPEG2VIDEO:
833    fill = "mpeg2";
834    break;
835  default:
836    fill = ctx->codec->name;
837    break;
838  }
839  snprintf(buf, len, "%s", fill);
840}
841
842
843/**
844 *
845 */
846static void
847codec_details(AVCodecContext *ctx, char *buf, size_t size, const char *lead)
848{
849  const char *cfg;
850
851  if(ctx->codec_type == CODEC_TYPE_AUDIO) {
852
853    if(ctx->sample_rate % 1000 == 0) {
854      snprintf(buf, size, "%s%d kHz", lead, ctx->sample_rate / 1000);
855    } else {
856      snprintf(buf, size, "%s%.1f kHz", lead, (float)ctx->sample_rate / 1000);
857    }
858    lead = ", ";
859
860    switch(ctx->channels) {
861    case 1:
862      cfg = "mono";
863      break;
864    case 2:
865      cfg = "stereo";
866      break;
867    case 6:
868      cfg = "5.1";
869      break;
870    default:
871      snprintf(buf + strlen(buf), size - strlen(buf), ", %d channels",
872               ctx->channels);
873      cfg = NULL;
874      break;
875    }
876    if(cfg != NULL) {
877      snprintf(buf + strlen(buf), size - strlen(buf), ", %s", cfg);
878    }
879  }
880
881  if(ctx->width) {
882    snprintf(buf + strlen(buf), size - strlen(buf),
883             "%s%dx%d", lead, ctx->width, ctx->height);
884    lead = ", ";
885  }
886
887  if(ctx->bit_rate > 2000000)
888    snprintf(buf + strlen(buf), size - strlen(buf),
889             "%s%.1f Mb/s", lead, (float)ctx->bit_rate / 1000000);
890  else if(ctx->bit_rate)
891    snprintf(buf + strlen(buf), size - strlen(buf),
892             "%s%d kb/s", lead, ctx->bit_rate / 1000);
893}
894
895/**
896 * Update codec info in property
897 */
898void
899media_update_codec_info_prop(prop_t *p, AVCodecContext *ctx)
900{
901  char tmp[100];
902
903  if(ctx == NULL) {
904    tmp[0] = 0;
905  } else {
906    snprintf(tmp, sizeof(tmp), "%s", ctx->codec->long_name);
907    codec_details(ctx, tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), ", ");
908  }
909  prop_set_string(p, tmp);
910}
911
912
913/**
914 * Update codec info in text widgets
915 */
916void
917media_get_codec_info(AVCodecContext *ctx, char *buf, size_t size)
918{
919  snprintf(buf, size, "%s\n", ctx->codec->long_name);
920  codec_details(ctx, buf + strlen(buf), size - strlen(buf), "");
921}
922
923
924/**
925 *
926 */
927static void
928seek_by_propchange(void *opaque, prop_event_t event, ...)
929{
930  event_ts_t *ets;
931  event_t *e;
932  media_pipe_t *mp = opaque;
933  int64_t t;
934
935  va_list ap;
936  va_start(ap, event);
937
938  switch(event) {
939  case PROP_SET_INT:
940    t = va_arg(ap, int) * 1000000LL;
941    break;
942  case PROP_SET_FLOAT:
943    t = va_arg(ap, double) * 1000000.0;
944    break;
945  default:
946    return;
947  }
948
949  /* If there already is a seek event enqueued, update it */
950  TAILQ_FOREACH(e, &mp->mp_eq, e_link) {
951    if(!event_is_type(e, EVENT_SEEK))
952      continue;
953
954    ets = (event_ts_t *)e;
955    ets->pts = t;
956    return;
957  }
958
959  ets = event_create(EVENT_SEEK, sizeof(event_ts_t));
960  ets->pts = t;
961  mp_enqueue_event_locked(mp, &ets->h);
962  event_unref(&ets->h);
963}
964
965
966/**
967 *
968 */
969static void
970update_avdelta(void *opaque, prop_event_t event, ...)
971{
972  media_pipe_t *mp = opaque;
973  int t;
974
975  va_list ap;
976  va_start(ap, event);
977
978  switch(event) {
979  case PROP_SET_INT:
980    t = va_arg(ap, int);
981    break;
982  case PROP_SET_FLOAT:
983    t = va_arg(ap, double);
984    break;
985  default:
986    return;
987  }
988
989  mp->mp_avdelta = t * 1000;
990  TRACE(TRACE_DEBUG, "AVSYNC", "Set to %d ms", t);
991}
992
993
994/**
995 *
996 */
997void
998mp_set_current_time(media_pipe_t *mp, int64_t pts)
999{
1000  if(pts != AV_NOPTS_VALUE)
1001    prop_set_float_ex(mp->mp_prop_currenttime, mp->mp_sub_currenttime,
1002                      pts / 1000000.0);
1003  else
1004    prop_set_void_ex(mp->mp_prop_currenttime, mp->mp_sub_currenttime);
1005}
1006
1007
1008/**
1009 *
1010 */
1011static void
1012media_eventsink(void *opaque, prop_event_t event, ...)
1013{
1014  event_t *e;
1015
1016  va_list ap;
1017  va_start(ap, event);
1018
1019  if(event != PROP_EXT_EVENT)
1020    return;
1021
1022  e = va_arg(ap, event_t *);
1023
1024  if(media_primary != NULL) {
1025    mp_enqueue_event(media_primary, e);
1026  } else {
1027    playqueue_event_handler(e);
1028  }
1029}
1030
1031/**
1032 *
1033 */
1034void
1035mp_set_playstatus_by_hold(media_pipe_t *mp, int hold)
1036{
1037  prop_set_string(mp->mp_prop_playstatus, hold ? "pause" : "play");
1038}
1039
1040
1041/**
1042 *
1043 */
1044void
1045mp_set_playstatus_stop(media_pipe_t *mp)
1046{
1047  prop_set_string(mp->mp_prop_playstatus, "stop");
1048}
1049
1050/**
1051 *
1052 */
1053void
1054mp_set_url(media_pipe_t *mp, const char *url)
1055{
1056  prop_set_string(mp->mp_prop_url, url);
1057}
1058
1059/**
1060 *
1061 */
1062void
1063mp_set_play_caps(media_pipe_t *mp, int caps)
1064{
1065  prop_set_int(mp->mp_prop_canSeek,  caps & MP_PLAY_CAPS_SEEK  ? 1 : 0);
1066  prop_set_int(mp->mp_prop_canPause, caps & MP_PLAY_CAPS_PAUSE ? 1 : 0);
1067  prop_set_int(mp->mp_prop_canEject, caps & MP_PLAY_CAPS_EJECT ? 1 : 0);
1068}
Note: See TracBrowser for help on using the browser.