* lukaszmluki/master: lavd/pulse_audio_enc: respect minreq while checking buffer fullness lavd/pulse_audio_enc: signal that buffer is still writable after write lavd/pulse_audio_enc: add pointer checks lavd/pulse_audio_enc: add more buffer attributes lavd/fbdev_dec: implement fbdev_get_device_list callback lavd/fbdev_enc: move list device code to fbdev_common lavd/fbdev_enc: remove redundant assignments Merged-by: Michael Niedermayer <michaelni@gmx.at>tags/n2.3
| @@ -290,6 +290,20 @@ When both options are provided then the highest value is used | |||
| are set to 0 (which is default), the device will use the default | |||
| PulseAudio duration value. By default PulseAudio set buffer duration | |||
| to around 2 seconds. | |||
| @item prebuf | |||
| Specify pre-buffering size in bytes. The server does not start with | |||
| playback before at least @option{prebuf} bytes are available in the | |||
| buffer. By default this option is initialized to the same value as | |||
| @option{buffer_size} or @option{buffer_duration} (whichever is bigger). | |||
| @item minreq | |||
| Specify minimum request size in bytes. The server does not request less | |||
| than @option{minreq} bytes from the client, instead waits until the buffer | |||
| is free enough to request more bytes at once. It is recommended to not set | |||
| this option, which will initialize this to a value that is deemed sensible | |||
| by the server. | |||
| @end table | |||
| @subsection Examples | |||
| @@ -20,9 +20,13 @@ | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include <unistd.h> | |||
| #include <fcntl.h> | |||
| #include <sys/ioctl.h> | |||
| #include <stdlib.h> | |||
| #include "fbdev_common.h" | |||
| #include "libavutil/common.h" | |||
| #include "avdevice.h" | |||
| struct rgb_pixfmt_map_entry { | |||
| int bits_per_pixel; | |||
| @@ -65,3 +69,61 @@ const char* ff_fbdev_default_device() | |||
| return dev; | |||
| } | |||
| int ff_fbdev_get_device_list(AVDeviceInfoList *device_list) | |||
| { | |||
| struct fb_var_screeninfo varinfo; | |||
| struct fb_fix_screeninfo fixinfo; | |||
| char device_file[12]; | |||
| AVDeviceInfo *device = NULL; | |||
| int i, fd, ret = 0; | |||
| const char *default_device = ff_fbdev_default_device(); | |||
| if (!device_list) | |||
| return AVERROR(EINVAL); | |||
| for (i = 0; i <= 31; i++) { | |||
| snprintf(device_file, sizeof(device_file), "/dev/fb%d", i); | |||
| if ((fd = avpriv_open(device_file, O_RDWR)) < 0) | |||
| continue; | |||
| if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) == -1) | |||
| goto fail_device; | |||
| if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) == -1) | |||
| goto fail_device; | |||
| device = av_mallocz(sizeof(AVDeviceInfo)); | |||
| if (!device) { | |||
| ret = AVERROR(ENOMEM); | |||
| goto fail_device; | |||
| } | |||
| device->device_name = av_strdup(device_file); | |||
| device->device_description = av_strdup(fixinfo.id); | |||
| if (!device->device_name || !device->device_description) { | |||
| ret = AVERROR(ENOMEM); | |||
| goto fail_device; | |||
| } | |||
| if ((ret = av_dynarray_add_nofree(&device_list->devices, | |||
| &device_list->nb_devices, device)) < 0) | |||
| goto fail_device; | |||
| if (default_device && !strcmp(device->device_name, default_device)) { | |||
| device_list->default_device = device_list->nb_devices - 1; | |||
| default_device = NULL; | |||
| } | |||
| close(fd); | |||
| continue; | |||
| fail_device: | |||
| if (device) { | |||
| av_free(device->device_name); | |||
| av_free(device->device_description); | |||
| av_freep(&device); | |||
| } | |||
| if (fd >= 0) | |||
| close(fd); | |||
| if (ret < 0) | |||
| return ret; | |||
| } | |||
| return 0; | |||
| } | |||
| @@ -27,8 +27,12 @@ | |||
| #include <linux/fb.h> | |||
| #include "libavutil/pixfmt.h" | |||
| struct AVDeviceInfoList; | |||
| enum AVPixelFormat ff_get_pixfmt_from_fb_varinfo(struct fb_var_screeninfo *varinfo); | |||
| const char* ff_fbdev_default_device(void); | |||
| int ff_fbdev_get_device_list(struct AVDeviceInfoList *device_list); | |||
| #endif /* AVDEVICE_FBDEV_COMMON_H */ | |||
| @@ -205,6 +205,11 @@ static av_cold int fbdev_read_close(AVFormatContext *avctx) | |||
| return 0; | |||
| } | |||
| static int fbdev_get_device_list(AVFormatContext *s, AVDeviceInfoList *device_list) | |||
| { | |||
| return ff_fbdev_get_device_list(device_list); | |||
| } | |||
| #define OFFSET(x) offsetof(FBDevContext, x) | |||
| #define DEC AV_OPT_FLAG_DECODING_PARAM | |||
| static const AVOption options[] = { | |||
| @@ -227,6 +232,7 @@ AVInputFormat ff_fbdev_demuxer = { | |||
| .read_header = fbdev_read_header, | |||
| .read_packet = fbdev_read_packet, | |||
| .read_close = fbdev_read_close, | |||
| .get_device_list = fbdev_get_device_list, | |||
| .flags = AVFMT_NOFILE, | |||
| .priv_class = &fbdev_class, | |||
| }; | |||
| @@ -186,64 +186,7 @@ static av_cold int fbdev_write_trailer(AVFormatContext *h) | |||
| static int fbdev_get_device_list(AVFormatContext *s, AVDeviceInfoList *device_list) | |||
| { | |||
| struct fb_var_screeninfo varinfo; | |||
| struct fb_fix_screeninfo fixinfo; | |||
| char device_file[12]; | |||
| AVDeviceInfo *device = NULL; | |||
| int i, fd = -1, ret = 0; | |||
| const char *default_device = ff_fbdev_default_device(); | |||
| if (!device_list) | |||
| return AVERROR(EINVAL); | |||
| for (i = 0; i <= 31; i++) { | |||
| snprintf(device_file, sizeof(device_file), "/dev/fb%d", i); | |||
| if ((fd = avpriv_open(device_file, O_RDWR)) < 0) | |||
| continue; | |||
| if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) == -1) | |||
| goto fail_device; | |||
| if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) == -1) | |||
| goto fail_device; | |||
| device = av_mallocz(sizeof(AVDeviceInfo)); | |||
| if (!device) { | |||
| ret = AVERROR(ENOMEM); | |||
| goto fail_device; | |||
| } | |||
| device->device_name = av_strdup(device_file); | |||
| device->device_description = av_strdup(fixinfo.id); | |||
| if (!device->device_name || !device->device_description) { | |||
| ret = AVERROR(ENOMEM); | |||
| goto fail_device; | |||
| } | |||
| if ((ret = av_dynarray_add_nofree(&device_list->devices, | |||
| &device_list->nb_devices, device)) < 0) | |||
| goto fail_device; | |||
| if (default_device && !strcmp(device->device_name, default_device)) { | |||
| device_list->default_device = device_list->nb_devices - 1; | |||
| default_device = NULL; | |||
| } | |||
| close(fd); | |||
| fd = -1; | |||
| continue; | |||
| fail_device: | |||
| if (device) { | |||
| av_free(device->device_name); | |||
| av_free(device->device_description); | |||
| av_freep(&device); | |||
| } | |||
| if (fd >= 0) { | |||
| close(fd); | |||
| fd = -1; | |||
| } | |||
| if (ret < 0) | |||
| return ret; | |||
| } | |||
| return 0; | |||
| return ff_fbdev_get_device_list(device_list); | |||
| } | |||
| #define OFFSET(x) offsetof(FBDevContext, x) | |||
| @@ -38,6 +38,8 @@ typedef struct PulseData { | |||
| int64_t timestamp; | |||
| int buffer_size; /**< Buffer size in bytes */ | |||
| int buffer_duration; /**< Buffer size in ms, recalculated to buffer_size */ | |||
| int prebuf; | |||
| int minreq; | |||
| int last_result; | |||
| pa_threaded_mainloop *mainloop; | |||
| pa_context *ctx; | |||
| @@ -475,6 +477,10 @@ static av_cold int pulse_write_header(AVFormatContext *h) | |||
| av_log(s, AV_LOG_DEBUG, "Real buffer length is %u bytes\n", buffer_attributes.tlength); | |||
| } else if (s->buffer_size) | |||
| buffer_attributes.tlength = s->buffer_size; | |||
| if (s->prebuf) | |||
| buffer_attributes.prebuf = s->prebuf; | |||
| if (s->minreq) | |||
| buffer_attributes.minreq = s->minreq; | |||
| sample_spec.format = ff_codec_id_to_pulse_format(st->codec->codec_id); | |||
| sample_spec.rate = st->codec->sample_rate; | |||
| @@ -578,6 +584,14 @@ static av_cold int pulse_write_header(AVFormatContext *h) | |||
| goto fail; | |||
| } | |||
| /* read back buffer attributes for future use */ | |||
| buffer_attributes = *pa_stream_get_buffer_attr(s->stream); | |||
| s->buffer_size = buffer_attributes.tlength; | |||
| s->prebuf = buffer_attributes.prebuf; | |||
| s->minreq = buffer_attributes.minreq; | |||
| av_log(s, AV_LOG_DEBUG, "Real buffer attributes: size: %d, prebuf: %d, minreq: %d\n", | |||
| s->buffer_size, s->prebuf, s->minreq); | |||
| pa_threaded_mainloop_unlock(s->mainloop); | |||
| if ((ret = pulse_subscribe_events(s)) < 0) { | |||
| @@ -610,6 +624,7 @@ static int pulse_write_packet(AVFormatContext *h, AVPacket *pkt) | |||
| { | |||
| PulseData *s = h->priv_data; | |||
| int ret; | |||
| int64_t writable_size; | |||
| if (!pkt) | |||
| return pulse_flash_stream(s); | |||
| @@ -632,7 +647,7 @@ static int pulse_write_packet(AVFormatContext *h, AVPacket *pkt) | |||
| av_log(s, AV_LOG_ERROR, "PulseAudio stream is in invalid state.\n"); | |||
| goto fail; | |||
| } | |||
| while (!pa_stream_writable_size(s->stream)) { | |||
| while (pa_stream_writable_size(s->stream) < s->minreq) { | |||
| if (s->nonblocking) { | |||
| pa_threaded_mainloop_unlock(s->mainloop); | |||
| return AVERROR(EAGAIN); | |||
| @@ -644,6 +659,9 @@ static int pulse_write_packet(AVFormatContext *h, AVPacket *pkt) | |||
| av_log(s, AV_LOG_ERROR, "pa_stream_write failed: %s\n", pa_strerror(ret)); | |||
| goto fail; | |||
| } | |||
| if ((writable_size = pa_stream_writable_size(s->stream)) >= s->minreq) | |||
| avdevice_dev_to_app_control_message(h, AV_DEV_TO_APP_BUFFER_WRITABLE, &writable_size, sizeof(writable_size)); | |||
| pa_threaded_mainloop_unlock(s->mainloop); | |||
| return 0; | |||
| @@ -678,8 +696,10 @@ static void pulse_get_output_timestamp(AVFormatContext *h, int stream, int64_t * | |||
| pa_threaded_mainloop_lock(s->mainloop); | |||
| pa_stream_get_latency(s->stream, &latency, &neg); | |||
| pa_threaded_mainloop_unlock(s->mainloop); | |||
| *wall = av_gettime(); | |||
| *dts = s->timestamp - (neg ? -latency : latency); | |||
| if (wall) | |||
| *wall = av_gettime(); | |||
| if (dts) | |||
| *dts = s->timestamp - (neg ? -latency : latency); | |||
| } | |||
| static int pulse_get_device_list(AVFormatContext *h, AVDeviceInfoList *device_list) | |||
| @@ -745,6 +765,8 @@ static const AVOption options[] = { | |||
| { "device", "set device name", OFFSET(device), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, | |||
| { "buffer_size", "set buffer size in bytes", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, | |||
| { "buffer_duration", "set buffer duration in millisecs", OFFSET(buffer_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, | |||
| { "prebuf", "set pre-buffering size", OFFSET(prebuf), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, | |||
| { "minreq", "set minimum request size", OFFSET(minreq), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, | |||
| { NULL } | |||
| }; | |||