当前位置: 首页 > news >正文

扩展tinyplay使其自适应不同声道数量的媒体

android原来的tinyplay代码,如果遇到播放媒体的 声道数量与打开pcm的声道数量不匹配的情况,会没法继续播放。

本例扩展了tinyplay的代码,将不同声道的音频数据展开/压缩到pcm设备支持的数据,再写入pcm设备。

 

bplay.c

#include <tinyalsa/asoundlib.h>#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <endian.h>
#include <unistd.h>
#include <pthread.h>#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT  0x20746d66
#define ID_DATA 0x61746164typedef enum {false = 0, true = 1} bool;struct riff_wave_header {uint32_t riff_id;uint32_t riff_sz;uint32_t wave_id;
};struct chunk_header {uint32_t id;uint32_t sz;
};struct chunk_fmt {uint16_t audio_format;uint16_t num_channels;uint32_t sample_rate;uint32_t byte_rate;uint16_t block_align;uint16_t bits_per_sample;
};#undef TAG
#define TAG "BPLAY"
#if 0      // def 0__ANDROID__
#include <android/log.h>
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#else
#define LOGI(str, ...) do{printf("BPLAY ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#define LOGD(str, ...) do{printf("BPLAY ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#define LOGW(str, ...) do{printf("BPLAY ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#define LOGE(str, ...) do{printf("BPLAY ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#endifstatic int isClosing = 0;void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int channels,unsigned int rate, unsigned int bits, unsigned int period_size,unsigned int period_count, uint32_t data_sz);void stream_close(int sig) {/* allow the stream to be closed gracefully */signal(sig, SIG_IGN);isClosing = 1;
}void* play_thread(void* arg) {FILE *file = NULL;struct chunk_header chunk_header;struct riff_wave_header riff_wave_header;struct chunk_fmt chunk_fmt;unsigned int device = 0;unsigned int card = 0;unsigned int period_size = 1024;unsigned int period_count = 4;const char *filename;int more_chunks = 1;char ** argv = (char **) arg;filename = argv[1];file = fopen(filename, "rb");if (!file) {LOGE("Unable to open file '%s'\n", filename);return (void*)(long)1;}fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);if ((riff_wave_header.riff_id != ID_RIFF) ||(riff_wave_header.wave_id != ID_WAVE)) {LOGI("Error: '%s' is not a riff/wave file\n", filename);fclose(file);return (void*)(long)1;}do {fread(&chunk_header, sizeof(chunk_header), 1, file);switch (chunk_header.id) {case ID_FMT:fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);/* If the format header is larger, skip the rest */if (chunk_header.sz > sizeof(chunk_fmt))fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);break;case ID_DATA:/* Stop looking for chunks */more_chunks = 0;chunk_header.sz = le32toh(chunk_header.sz);break;default:/* Unknown chunk, skip bytes */fseek(file, chunk_header.sz, SEEK_CUR);}} while (more_chunks);/* parse command line arguments */argv += 2;while (*argv) {if (strcmp(*argv, "-d") == 0) {argv++;if (*argv)device = atoi(*argv);}if (strcmp(*argv, "-p") == 0) {argv++;if (*argv)period_size = atoi(*argv);}if (strcmp(*argv, "-n") == 0) {argv++;if (*argv)period_count = atoi(*argv);}if (strcmp(*argv, "-D") == 0) {argv++;if (*argv)card = atoi(*argv);}if (*argv)argv++;}play_sample(file, card, device, chunk_fmt.num_channels, chunk_fmt.sample_rate,chunk_fmt.bits_per_sample, period_size, period_count, chunk_header.sz);fclose(file);return (void*)(long)0;
}int main(int argc, char* argv[]) {pthread_t tid;void* status;if (argc < 2) {LOGI("Usage: %s file.wav [-D card] [-d device] [-p period_size]"" [-n n_periods] \n", argv[0]);return 1;}if (pthread_create(&tid, NULL, play_thread, (void*)argv)) {return 1;}pthread_join(tid, &status);return 0;
}int check_param(struct pcm_params *params, unsigned int param, unsigned int value,char *param_name, char *param_unit) {unsigned int min;unsigned int max;int is_within_bounds = 1;min = pcm_params_get_min(params, param);if (value < min) {//LOGW("%s is %u%s, device supports >= %u%s\n", param_name, value, param_unit, min, param_unit);//is_within_bounds = 0;}max = pcm_params_get_max(params, param);if (value > max) {LOGE("%s is %u%s, device only supports <= %u%s\n", param_name, value,param_unit, max, param_unit);is_within_bounds = 0;}return is_within_bounds;
}int sample_is_playable(unsigned int card, unsigned int device, unsigned int channels,unsigned int rate, unsigned int bits, unsigned int period_size,unsigned int period_count) {struct pcm_params *params;int can_play;params = pcm_params_get(card, device, PCM_OUT);if (params == NULL) {LOGE("Unable to open PCM device %u.\n", device);return 0;}can_play = check_param(params, PCM_PARAM_RATE, rate, "Sample rate", "Hz");can_play &= check_param(params, PCM_PARAM_CHANNELS, channels, "Sample", " channels");can_play &= check_param(params, PCM_PARAM_SAMPLE_BITS, bits, "Bitrate", " bits");can_play &= check_param(params, PCM_PARAM_PERIOD_SIZE, period_size, "Period size", " frames");can_play &= check_param(params, PCM_PARAM_PERIODS, period_count, "Period count", " periods");pcm_params_free(params);return can_play;
}static int merge_data_to_channel(char* out_buffer, const char* in_buffer, int src_size,int src_channels, int dest_channels, int bit_width) {int i = 0;int copy_channels = src_channels;if (src_channels > dest_channels) copy_channels = dest_channels;int loop_count = src_size / bit_width / src_channels;for(; i < loop_count; i ++) {memcpy(out_buffer + i * dest_channels * bit_width,in_buffer + i * bit_width * src_channels,bit_width * copy_channels);}return src_size * dest_channels / src_channels;
}void play_sample(FILE *file, unsigned int card, unsigned int device, const unsigned int channels,unsigned int rate, unsigned int bits, unsigned int period_size,unsigned int period_count, uint32_t data_sz) {struct pcm_config config;struct pcm *pcm = NULL;char *write_buffer = NULL;char *read_buffer = NULL;unsigned int buffer_size = 0, read_sz;int num_read;uint32_t frame_count = 0; //bose adduint32_t loop_times = 0;uint32_t byte_rate = 1;uint32_t total_seconds = 0;uint32_t current_seconds = 0;uint32_t min_channel = 0;struct pcm_params *params;int bit_width = 1;bit_width = bits / 8;memset(&config, 0, sizeof(config));params = pcm_params_get(card, device, PCM_OUT);min_channel = pcm_params_get_min(params, PCM_PARAM_CHANNELS);if (channels < min_channel) {LOGW("convert config.channels to %d instead of %d", min_channel, channels);config.channels = min_channel;} else {config.channels = channels;}config.rate = rate;config.period_size = period_size;config.period_count = period_count;if (bits == 32)config.format = PCM_FORMAT_S32_LE;else if (bits == 24)config.format = PCM_FORMAT_S24_3LE;else if (bits == 16)config.format = PCM_FORMAT_S16_LE;config.start_threshold = 0;config.stop_threshold = 0;config.silence_threshold = 0;do {if (!sample_is_playable(card, device, channels, rate, bits, period_size, period_count)) {LOGE("sample is not playable");break;}pcm = pcm_open(card, device, PCM_OUT, &config);if (!pcm || !pcm_is_ready(pcm)) {LOGE("Unable to open PCM device %u (%s)\n",device, pcm_get_error(pcm));break;}buffer_size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));write_buffer = (char*)malloc(buffer_size);memset(write_buffer, 0, buffer_size);read_buffer = (char*)malloc(buffer_size);if (!write_buffer || !read_buffer) {LOGE("Unable to allocate %d bytes\n", buffer_size * 2);free(write_buffer);free(read_buffer);pcm_close(pcm);break;}} while(0);byte_rate = (uint32_t) ((channels * rate * bits) / 8);total_seconds = (uint32_t)(data_sz / byte_rate);LOGD(">>> %u ch, %u hz, %u bit byte_rate=%u. Total %u bytes, %u seconds.",channels, rate, bits, byte_rate, data_sz, total_seconds);LOGD(">>> pcm_get_buffer_size(): %d, write_size: %d, read size:%d",pcm_get_buffer_size(pcm), buffer_size, buffer_size * channels / min_channel);/* catch ctrl-c to shutdown cleanly */signal(SIGINT, stream_close);do {float t = total_seconds - data_sz / byte_rate;if ((t - current_seconds) >= 1.000f) {current_seconds = (uint32_t)t;printf("\rPlayed %d seconds, [%d]bytes remain.", current_seconds, data_sz);fflush(stdout);}memset(read_buffer, 0, buffer_size);memset(write_buffer, 0, buffer_size);read_sz = buffer_size * channels / min_channel < data_sz ? buffer_size * channels / min_channel : data_sz;num_read = fread(read_buffer, 1, read_sz, file);if (num_read > 0) {merge_data_to_channel(write_buffer, read_buffer, read_sz, channels, config.channels, bit_width);if (pcm_write(pcm, write_buffer, buffer_size)) {LOGE("Error playing sample, remain bytes=%d, loop=%d", data_sz, loop_times);// receiving signal may interrupt some system call.}frame_count++;data_sz -= num_read;}} while (!isClosing && num_read > 0 && data_sz > 0);printf("\nEnd playing.\n");fflush(stdout);free(write_buffer);free(read_buffer);if(NULL != pcm)pcm_close(pcm);
}

A reference Android.mk

LOCAL_PATH := $(call my-dir)################################### /system/bin/bplay ########################################
include $(CLEAR_VARS)
LOCAL_MODULE := bplay
LOCAL_CFLAGS := -Werror -g -o0
LOCAL_LDFLAGS += -Wl
LOCAL_CPPFLAGS := -Werror -Wfloat-equal -Wformat=2LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES += bplay.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)LOCAL_SHARED_LIBRARIES :=liblog libutils libcutils \libtinyalsaLOCAL_STATIC_LIBRARIES +=include $(BUILD_EXECUTABLE)

 

 


http://www.mrgr.cn/news/80215.html

相关文章:

  • 【数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
  • Ubuntu18安装后基本配置操作
  • 全方位解析双 Token实现无感刷新:用 Spring Boot + Vue + Redis 构建高安全认证体系
  • JavaScript 面向对象编程的深度探索与实践
  • React的状态管理库-Redux
  • OpenCV相机标定与3D重建(14)用于组合两个旋转和平移(R|T)变换函数composeRT()的使用
  • 17、ConvMixer模型原理及其PyTorch逐行实现
  • 网络工程师常用软件之配置对比软件
  • 【kubernetes】kubectl get nodes报NotReady
  • 文件的读写
  • Docker中 localhost 与 0.0.0.0 的区别详解
  • Deveco Studio首次编译项目初始化失败
  • boost电路的同步和异步模式 及CCM、DCM模式 介绍
  • 计算机网络-传输层 TCP协议(上)
  • IOS通过WDA自动化中遇到的问题
  • 进制的转换
  • 【C++11】类的新功能
  • JVM系列之内存区域
  • SEGGER | 基于STM32F405 + Keil - RTT组件02 - RTT Viewer替代串口调试,实时打印调试log
  • 【通信网络】二层基础:02 VLAN基础之一
  • 深入理解RSA算法:核心概念与原理详解
  • Linux shell的七大功能 --- history
  • 测试工程师八股文04|计算机网络 和 其他
  • MySQL 存储过程与函数:增强数据库功能
  • Quad Remesher使用教程
  • 区间和并—acwing