]> git.alsa-project.org Git - tinycompress.git/commitdiff
fcplay: initial addition
authorVinod Koul <vkoul@kernel.org>
Thu, 5 Dec 2019 07:57:28 +0000 (13:27 +0530)
committerJaroslav Kysela <perex@perex.cz>
Wed, 19 Feb 2020 09:30:44 +0000 (10:30 +0100)
This tool was forked from tinycompress cplay. The fork was required due
to this linking against libavcodec which is GPL. This uses libav APIs to
parse the compressed files and send the audio stream to compressed
device using tinycompress APIs

Right now it supports only playback on MP3 (no problems now not finding
sync word) and flac files.

Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
.gitignore
configure.ac
src/Makefile.am
src/utils-lgpl/Makefile.am [new file with mode: 0644]
src/utils-lgpl/fcplay.c [new file with mode: 0644]

index 581b31038c8ff1ee4a1558fd5d2ab7a7f924bf1d..6831c3d85bbac7fe1dcf1050dc11447da3c373a6 100644 (file)
@@ -32,8 +32,10 @@ m4/lt~obsolete.m4
 # binary ignores
 cplay
 crecord
+fcplay
 
 # other ignores
 *.patch
 cscope.*
 tags
+tinycompress.pc
index ce51d5ce21b31b999196c0ead85f63100b8d153e..449ec6e16960499d21ed92399a85ce0118dfc418 100644 (file)
@@ -9,6 +9,16 @@ AC_CONFIG_MACRO_DIR([m4])
 AM_INIT_AUTOMAKE(1.10)
 LT_INIT(disable-static)
 
+AC_ARG_ENABLE(fcplay,
+  AS_HELP_STRING([--enable-fcplay], [enable the fcplay component]),
+  [build_fcplay="$enableval"], [build_fcplay="no"])
+
+AM_CONDITIONAL([BUILD_FCPLAY], [test x$build_fcplay = xyes])
+
+#if test "$build_fcplay" = "yes"; then
+#  AC_DEFINE([BUILD_FCPLAY], "1", [Build Fcplay component])
+#fi
+
 # Checks for programs.
 AC_PROG_CXX
 AC_PROG_CC
@@ -19,6 +29,11 @@ AM_PROG_CC_C_O
 PKG_INSTALLDIR
 
 # Checks for libraries.
+AS_IF([test "x$build_fcplay" = "xyes"], [
+  PKG_CHECK_MODULES([AVCODEC], [libavcodec >= 3.0.7])
+  PKG_CHECK_MODULES([AVFORMAT], [libavformat >= 3.0.7])
+  PKG_CHECK_MODULES([AVUTIL], [libavutil >= 3.0.7])
+])
 
 # Checks for typedefs, structures, and compiler characteristics.
 
@@ -30,5 +45,6 @@ include/Makefile
 src/Makefile
 src/lib/Makefile
 src/utils/Makefile
+src/utils-lgpl/Makefile
 tinycompress.pc])
 AC_OUTPUT
index be97404412eb48353908244ff98d486b2e36ac06..4a8c867f262cd3d391bd59dcca388850a09552a8 100644 (file)
@@ -1 +1,4 @@
 SUBDIRS = lib utils
+if BUILD_FCPLAY
+SUBDIRS += utils-lgpl
+endif
diff --git a/src/utils-lgpl/Makefile.am b/src/utils-lgpl/Makefile.am
new file mode 100644 (file)
index 0000000..c78d708
--- /dev/null
@@ -0,0 +1,15 @@
+bin_PROGRAMS = fcplay
+
+fcplay_SOURCES = fcplay.c
+
+fcplay_CFLAGS = -I$(top_srcdir)/include \
+                   $(AVCODEC_CFLAGS) \
+                   $(AVFORMAT_CFLAGS) \
+                   $(AVUTIL_CFLAGS) \
+                   $(LIBDRM_CFLAGS)
+
+fcplay_LDADD = $(top_builddir)/src/lib/libtinycompress.la \
+                   $(AVCODEC_LIBS) \
+                   $(AVFORMAT_LIBS) \
+                   $(AVUTIL_LIBS) \
+                   $(LIBDRM_LIBS)
diff --git a/src/utils-lgpl/fcplay.c b/src/utils-lgpl/fcplay.c
new file mode 100644 (file)
index 0000000..83d48a0
--- /dev/null
@@ -0,0 +1,396 @@
+//SPDX-License-Identifier: LGPL-2.1-only
+
+//Copyright (c) 2011-2012, Intel Corporation
+//Copyright (c) 2018-2019, Linaro Ltd
+
+#include <stdint.h>
+#include <linux/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <sys/time.h>
+#define __force
+#define __user
+#include "sound/compress_params.h"
+#include "tinycompress/tinycompress.h"
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+
+static int verbose;
+
+static const struct {
+       const char *name;
+       unsigned int id;
+} codec_ids[] = {
+       { "PCM", SND_AUDIOCODEC_PCM },
+       { "MP3", SND_AUDIOCODEC_MP3 },
+       { "AMR", SND_AUDIOCODEC_AMR },
+       { "AMRWB", SND_AUDIOCODEC_AMRWB },
+       { "AMRWBPLUS", SND_AUDIOCODEC_AMRWBPLUS },
+       { "AAC", SND_AUDIOCODEC_AAC },
+       { "WMA", SND_AUDIOCODEC_WMA },
+       { "REAL", SND_AUDIOCODEC_REAL },
+       { "VORBIS", SND_AUDIOCODEC_VORBIS },
+       { "FLAC", SND_AUDIOCODEC_FLAC },
+       { "IEC61937", SND_AUDIOCODEC_IEC61937 },
+       { "G723_1", SND_AUDIOCODEC_G723_1 },
+       { "G729", SND_AUDIOCODEC_G729 },
+/* BESPOKE isn't defined on older kernels */
+#ifdef SND_AUDIOCODEC_BESPOKE
+       { "BESPOKE", SND_AUDIOCODEC_BESPOKE },
+#endif
+};
+#define CPLAY_NUM_CODEC_IDS (sizeof(codec_ids) / sizeof(codec_ids[0]))
+
+static void usage(void)
+{
+       int i;
+
+       fprintf(stderr, "usage: cplay [OPTIONS] filename\n"
+               "-c\tcard number\n"
+               "-d\tdevice node\n"
+               "-I\tspecify codec ID (default is mp3)\n"
+               "-b\tbuffer size\n"
+               "-f\tfragments\n\n"
+               "-v\tverbose mode\n"
+               "-h\tPrints this help list\n\n"
+               "Example:\n"
+               "\tfcplay -c 1 -d 2 test.mp3\n"
+               "\tfcplay -f 5 test.mp3\n\n"
+               "Valid codec IDs:\n");
+
+       for (i = 0; i < CPLAY_NUM_CODEC_IDS; ++i)
+               fprintf(stderr, "%s%c", codec_ids[i].name,
+                       ((i + 1) % 8) ? ' ' : '\n');
+
+       fprintf(stderr, "\nor the value in decimal or hex\n");
+
+       exit(EXIT_FAILURE);
+}
+
+void play_samples(char *name, unsigned int card, unsigned int device,
+               unsigned long buffer_size, unsigned int frag,
+               unsigned long codec_id);
+
+static int print_time(struct compress *compress)
+{
+       unsigned int avail;
+       struct timespec tstamp;
+
+       if (compress_get_hpointer(compress, &avail, &tstamp) != 0) {
+               fprintf(stderr, "Error querying timestamp\n");
+               fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
+               return -1;
+       } else
+               fprintf(stderr, "DSP played %jd.%jd\n", (intmax_t)tstamp.tv_sec, (intmax_t)tstamp.tv_nsec*1000);
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       char *file;
+       unsigned long buffer_size = 0;
+       int c, i;
+       unsigned int card = 0, device = 0, frag = 0;
+       unsigned int codec_id = SND_AUDIOCODEC_MP3;
+
+       if (argc < 2)
+               usage();
+
+       verbose = 0;
+       while ((c = getopt(argc, argv, "hvb:f:c:d:I:")) != -1) {
+               switch (c) {
+               case 'h':
+                       usage();
+                       break;
+               case 'b':
+                       buffer_size = strtol(optarg, NULL, 0);
+                       break;
+               case 'f':
+                       frag = strtol(optarg, NULL, 10);
+                       break;
+               case 'c':
+                       card = strtol(optarg, NULL, 10);
+                       break;
+               case 'd':
+                       device = strtol(optarg, NULL, 10);
+                       break;
+               case 'I':
+                       if (optarg[0] == '0') {
+                               codec_id = strtol(optarg, NULL, 0);
+                       } else {
+                               for (i = 0; i < CPLAY_NUM_CODEC_IDS; ++i) {
+                                       if (strcmp(optarg,
+                                                  codec_ids[i].name) == 0) {
+                                               codec_id = codec_ids[i].id;
+                                               break;
+                                       }
+                               }
+
+                               if (i == CPLAY_NUM_CODEC_IDS) {
+                                       fprintf(stderr, "Unrecognised ID: %s\n",
+                                               optarg);
+                                       usage();
+                               }
+                       }
+                       break;
+               case 'v':
+                       verbose = 1;
+                       break;
+               default:
+                       exit(EXIT_FAILURE);
+               }
+       }
+       if (optind >= argc)
+               usage();
+
+       file = argv[optind];
+
+       play_samples(file, card, device, buffer_size, frag, codec_id);
+
+       fprintf(stderr, "Finish Playing.... Close Normally\n");
+       exit(EXIT_SUCCESS);
+}
+
+static int get_codec_id(int codec_id)
+{
+       switch (codec_id) {
+       case AV_CODEC_ID_MP3:
+               return SND_AUDIOCODEC_MP3;
+       case AV_CODEC_ID_AAC:
+               return SND_AUDIOCODEC_AAC;
+       case AV_CODEC_ID_WMAV1:
+       case AV_CODEC_ID_WMAV2:
+               return SND_AUDIOCODEC_WMA;
+       case AV_CODEC_ID_VORBIS:
+               return SND_AUDIOCODEC_VORBIS;
+       case AV_CODEC_ID_FLAC:
+               return SND_AUDIOCODEC_FLAC;
+       case AV_CODEC_ID_RA_144:
+       case AV_CODEC_ID_RA_288:
+               return SND_AUDIOCODEC_REAL;
+       case AV_CODEC_ID_AMR_NB:
+               return SND_AUDIOCODEC_AMR;
+       case AV_CODEC_ID_AMR_WB:
+               return SND_AUDIOCODEC_AMRWB;
+       case AV_CODEC_ID_PCM_S16LE ... AV_CODEC_ID_PCM_S16BE_PLANAR:
+               return SND_AUDIOCODEC_PCM;
+       default:
+               fprintf(stderr, "Not supported AVcodec: %d\n", codec_id);
+               exit(EXIT_FAILURE);
+       }
+}
+
+static int parse_file(char *file, struct snd_codec *codec)
+{
+       AVFormatContext *ctx = NULL;
+       AVStream *stream;
+       char errbuf[50];
+       int err = 0, i, filled = 0;
+
+       err = avformat_open_input(&ctx, file, NULL, NULL);
+       if (err < 0) {
+               av_strerror(err, errbuf, sizeof(errbuf));
+               fprintf(stderr, "Unable to open %s: %s\n", file, errbuf);
+               exit(EXIT_FAILURE);
+       }
+
+       err = avformat_find_stream_info(ctx, NULL);
+       if (err < 0) {
+               av_strerror(err, errbuf, sizeof(errbuf));
+               fprintf(stderr, "Unable to identify %s: %s\n", file, errbuf);
+               goto exit;
+       }
+
+       if (ctx->nb_streams < 1) {
+               fprintf(stderr, "No streams found in %s\n", file);
+               goto exit;
+       }
+
+       if (verbose)
+               fprintf(stderr, "Streams: %d\n", ctx->nb_streams);
+
+       for (i = 0; i < ctx->nb_streams; i++) {
+               stream =  ctx->streams[i];
+
+               if (verbose)
+                       fprintf(stderr, "StreamType: %d", stream->codecpar->codec_type);
+
+               if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+                       fprintf(stderr, "Stream:%d is audio type\n", i);
+
+                       if (!filled) {
+                               /* we fill params from 1st audio stream */
+                               codec->id = get_codec_id(stream->codecpar->codec_id);
+                               codec->ch_in = stream->codecpar->channels;
+                               codec->ch_out = stream->codecpar->channels;
+                               codec->sample_rate = stream->codecpar->sample_rate;
+                               codec->bit_rate = ctx->bit_rate;
+                               codec->profile = stream->codecpar->profile;
+                               codec->format = 0; /* need codec format type */
+                               codec->align = stream->codecpar->block_align;
+                               codec->level = 0;
+                               codec->rate_control = 0;
+                               codec->ch_mode = 0;
+                               filled = 1;
+
+                               if (codec->id == SND_AUDIOCODEC_FLAC) {
+                                       codec->options.flac_d.sample_size = stream->codecpar->bits_per_raw_sample;
+                                       /* use these values from libav/flac* where 16 is mind block size for
+                                        * flac and 64K max. 11 is min frame and avg is 8192, so take 4 times
+                                        * that
+                                        */
+                                       codec->options.flac_d.min_blk_size = 16;
+                                       codec->options.flac_d.max_blk_size = 65535;
+                                       codec->options.flac_d.min_frame_size = 11;
+                                       codec->options.flac_d.max_frame_size = 8192*4;
+                               }
+                       }
+
+                       if (verbose) {
+                               fprintf(stderr, "Stream:%d", i);
+                               fprintf(stderr, "  Codec: %d", stream->codecpar->codec_id);
+                               fprintf(stderr, "  Format: %d", stream->codecpar->format);
+                               fprintf(stderr, "  Bit Rate: %ld", stream->codecpar->bit_rate);
+                               fprintf(stderr, "  Bits coded: %d", stream->codecpar->bits_per_coded_sample);
+                               fprintf(stderr, "  Profile: %d", stream->codecpar->profile);
+                               fprintf(stderr, "  Codec tag: %d", stream->codecpar->codec_tag);
+                               fprintf(stderr, "  Channels: %d", stream->codecpar->channels);
+                               fprintf(stderr, "  Sample rate: %d", stream->codecpar->sample_rate);
+                               fprintf(stderr, "  block_align: %d", stream->codecpar->block_align);
+                               if (codec->id == SND_AUDIOCODEC_FLAC) {
+                                       fprintf(stderr, "  Sample Size %d",  codec->options.flac_d.sample_size);
+                                       fprintf(stderr, "  Min Block Size  %d",  codec->options.flac_d.min_blk_size);
+                                       fprintf(stderr, "  Max Block Size  %d",  codec->options.flac_d.max_blk_size);
+                                       fprintf(stderr, "  Min Frame Size  %d",  codec->options.flac_d.min_frame_size);
+                                       fprintf(stderr, "  Max Frame Size  %d",  codec->options.flac_d.max_frame_size);
+
+                               }
+                               fprintf(stderr, "\n");
+                       }
+               }
+       }
+
+       if (verbose)
+               av_dump_format(ctx, 0, file, 0);
+
+       avformat_close_input(&ctx);
+       return 0;
+
+exit:
+       avformat_close_input(&ctx);
+       exit(EXIT_FAILURE);
+
+}
+
+void play_samples(char *name, unsigned int card, unsigned int device,
+               unsigned long buffer_size, unsigned int frag,
+               unsigned long codec_id)
+{
+       struct compr_config config;
+       struct snd_codec codec;
+       struct compress *compress;
+       FILE *file;
+       char *buffer;
+       int size, num_read, wrote;
+
+       if (verbose)
+               printf("%s: entry\n", __func__);
+       file = fopen(name, "rb");
+       if (!file) {
+               fprintf(stderr, "Unable to open file '%s'\n", name);
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&codec, 0, sizeof(codec));
+       memset(&config, 0, sizeof(config));
+
+       parse_file(name, &codec);
+
+       config.codec = &codec;
+
+       compress = compress_open(card, device, COMPRESS_IN, &config);
+       if (!compress || !is_compress_ready(compress)) {
+               fprintf(stderr, "Unable to open Compress device %d:%d\n",
+                               card, device);
+               fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
+               goto FILE_EXIT;
+       };
+       if (verbose)
+               printf("%s: Opened compress device\n", __func__);
+       size = config.fragment_size;
+       buffer = malloc(size * config.fragments);
+       if (!buffer) {
+               fprintf(stderr, "Unable to allocate %d bytes\n", size);
+               goto COMP_EXIT;
+       }
+
+       /* we will write frag fragment_size and then start */
+       num_read = fread(buffer, 1, size * config.fragments, file);
+       if (num_read > 0) {
+               if (verbose)
+                       printf("%s: Doing first buffer write of %d\n", __func__, num_read);
+               wrote = compress_write(compress, buffer, num_read);
+               if (wrote < 0) {
+                       fprintf(stderr, "Error %d playing sample\n", wrote);
+                       fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
+                       goto BUF_EXIT;
+               }
+               if (wrote != num_read) {
+                       /* TODO: Buufer pointer needs to be set here */
+                       fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote);
+               }
+       }
+       printf("Playing file %s On Card %u device %u, with buffer of %lu bytes\n",
+                       name, card, device, buffer_size);
+       printf("Format %u Channels %u, %u Hz, Bit Rate %d\n",
+                       codec.id, codec.ch_in, codec.sample_rate, codec.bit_rate);
+
+       compress_start(compress);
+       if (verbose)
+               printf("%s: You should hear audio NOW!!!\n", __func__);
+
+       do {
+               num_read = fread(buffer, 1, size, file);
+               if (num_read > 0) {
+                       wrote = compress_write(compress, buffer, num_read);
+                       if (wrote < 0) {
+                               fprintf(stderr, "Error playing sample\n");
+                               fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
+                               goto BUF_EXIT;
+                       }
+                       if (wrote != num_read) {
+                               /* TODO: Buffer pointer needs to be set here */
+                               fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote);
+                       }
+                       if (verbose) {
+                               print_time(compress);
+                               printf("%s: wrote %d\n", __func__, wrote);
+                       }
+               }
+       } while (num_read > 0);
+
+       if (verbose)
+               printf("%s: exit success\n", __func__);
+       /* issue drain if it supports */
+       compress_drain(compress);
+       free(buffer);
+       fclose(file);
+       compress_close(compress);
+       return;
+BUF_EXIT:
+       free(buffer);
+COMP_EXIT:
+       compress_close(compress);
+FILE_EXIT:
+       fclose(file);
+       if (verbose)
+               printf("%s: exit failure\n", __func__);
+       exit(EXIT_FAILURE);
+}