SDL2播放PCM数据的两种方案

方案一

使用回调的方式,也就是pull模式

SDL_AudioSpec结构

typedef struct SDL_AudioSpec
{
    // 采样率
    int freq;                   /**< DSP frequency -- samples per second */
    // 音频数据格式 
    SDL_AudioFormat format;     /**< Audio data format */
    // 声道数
    Uint8 channels;             /**< Number of channels: 1 mono, 2 stereo */
    // 音频缓冲区静音值
    Uint8 silence;              /**< Audio buffer silence value (calculated) */
    // 采样帧大小
    Uint16 samples;             /**< Audio buffer size in sample FRAMES (total samples divided by channel count) */
    // 兼容性参数
    Uint16 padding;             /**< Necessary for some compile environments */
    // 音频缓冲区大小
    Uint32 size;                /**< Audio buffer size in bytes (calculated) */
    // 填充音频缓冲区回调函数
    SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
    // 用户自定义数据,
    void *userdata;             /**< Userdata passed to callback (ignored for NULL callbacks). */
} SDL_AudioSpec;

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sdl.h"

static  Uint8* audio_chunk;
static  Uint32  audio_len;
static  Uint8* audio_pos;

void  fill_audio(void* udata, Uint8* stream, int len) {
	//SDL 2.0
	SDL_memset(stream, 0, len);

	if (audio_len == 0)
		return;
	len = (len > audio_len ? audio_len : len);

	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_len -= len;
}


int main2(int argc, char* argv[])
{

	if (SDL_Init(SDL_INIT_AUDIO || SDL_INIT_TIMER))
	{
		printf("SDL init error\n");
		return -1;
	}


	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16LSB;
	wanted_spec.channels = 2;
	wanted_spec.silence = 0;
	wanted_spec.samples = 512;
	wanted_spec.callback = fill_audio;


	if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
		printf("can't open audio.\n");
		return -1;
	}

	FILE* fp = nullptr;
		
	fopen_s(&fp, "D:/工程/音视频分析/source/s16.pcm", "rb+");

	if (fp == NULL) {
		printf("cannot open this file\n");
		return -1;
	}
	int pcm_buffer_size = 2048;
	char* pcm_buffer = (char*)malloc(pcm_buffer_size);
	int data_count = 0;
	int readcount = 0;

	//Play
	SDL_PauseAudio(0);

	while (!feof(fp)) 
	{
		readcount = fread(pcm_buffer, 1, pcm_buffer_size, fp);
		

		printf("   Now Playing %10d KBytes data.  %d \n", data_count / 1024, readcount);
		data_count += readcount;
		//Set audio buffer (PCM data)
		audio_chunk = (Uint8*)pcm_buffer;
		//Audio buffer length
		audio_len = readcount;
		audio_pos = audio_chunk;

		while (audio_len > 0)//Wait until finish
			SDL_Delay(1);
		
	}

	free(pcm_buffer);

	SDL_Quit();

	return 0;
}

注意事项:

每次读取的数据 是 (  通道数 x 样本数 x 样本个数字节数) 整数倍

例如:通道数为2 ,每次样本数 1024, 样本深度 f32le(float 32 小端)

那么 pcm_buffer_size 至少是 2 x 1024 x 4 = 8192 个字节,或其他整数倍

方案二

主动添加音频数据到播放数据队列中,push模式

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sdl.h"

int main(int argc, char* argv[])
{

	if (SDL_Init(SDL_INIT_AUDIO))
	{
		printf("SDL init error\n");
		return -1;
	}


	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;
	wanted_spec.freq = 44100;
	wanted_spec.format = AUDIO_S16LSB;
	wanted_spec.channels = 2;
	wanted_spec.silence = 0;
	wanted_spec.samples = 512;
	wanted_spec.callback = nullptr;

	SDL_AudioDeviceID deviceID = 0;
	// 打开设备
	if ((deviceID = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) < 2) 
	{

		printf("could not open audio device: %s\n", SDL_GetError());
		
		// 清除所有的子系统
		SDL_Quit();
		return 0;
	}

	SDL_PauseAudioDevice(deviceID, 0);

	FILE* fp = nullptr;

	fopen_s(&fp, "D:/工程/音视频分析/source/s16.pcm", "rb+");
	if (fp == NULL) {
		printf("cannot open this file\n");
		return -1;
	}

	if (fp == NULL) {
		printf("error \n");
	}
	Uint32 buffer_size = 4096;
	char* buffer = (char*)malloc(buffer_size);

	while (true)
	{
		if (fread(buffer, 1, buffer_size, fp) != buffer_size) 
		{
			 printf("end of file\n");
			break;
		}
		SDL_QueueAudio(deviceID, buffer, buffer_size);
	}

	Uint32 residueAudioLen = 0;

	/*while (true)
	{
		residueAudioLen = SDL_GetQueuedAudioSize(deviceID);
		printf("%10d\n", residueAudioLen);

		if (residueAudioLen <= 0)
			break;
		SDL_Delay(1);
	}*/


	
	while (true)
	{

	
	printf("1 暂停 2 继续  3 退出 \n");
	int flag = 0;
		
		
	scanf_s("%d", &flag);

	if (flag == 1)
		SDL_PauseAudioDevice(deviceID, 1);
	else if (flag == 2)
		SDL_PauseAudioDevice(deviceID, 0);
	else if(flag == 3)
		break;
	}

	SDL_CloseAudio();
	SDL_Quit();
	fclose(fp);

	return 0;

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用SDL2在Linux上播放视频,需要先安装SDL2库和FFmpeg库。接下来,需要用FFmpeg将视频解码成yuv像素数据,再使用SDL2将像素数据渲染到屏幕上。 以下是基本的步骤: 1. 引入SDL2和FFmpeg库,定义相关变量和函数。 ```c #include <SDL2/SDL.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> AVCodecContext *codec_ctx; AVFormatContext *format_ctx; AVCodec *codec; SDL_Renderer* renderer; SDL_Texture* texture; SDL_Window *window; SDL_Rect rect; unsigned char *output_buffer; int pitch; struct SwsContext *sws_ctx; ``` 2. 初始化SDL2和FFmpeg库。 ```c if(SDL_Init(SDL_INIT_VIDEO) != 0) { printf("SDL_Init Error: %s", SDL_GetError()); return -1; } av_register_all(); avformat_network_init(); format_ctx = avformat_alloc_context(); if(avformat_open_input(&format_ctx, file_path, NULL, NULL) != 0) { return -1; } if(avformat_find_stream_info(format_ctx, NULL) < 0) { return -1; } int video_stream_index = -1; for(int i=0; i < format_ctx->nb_streams; i++) { if(format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_stream_index = i; break; } } if(video_stream_index == -1) { return -1; } codec_ctx = format_ctx->streams[video_stream_index]->codec; codec = avcodec_find_decoder(codec_ctx->codec_id); if(codec == NULL) { return -1; } if(avcodec_open2(codec_ctx, codec, NULL) < 0) { return -1; } ``` 3. 配置SDL2窗口、渲染器和纹理。 ```c window = SDL_CreateWindow("Video Player", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, codec_ctx->width, codec_ctx->height, SDL_WINDOW_OPENGL); renderer = SDL_CreateRenderer(window, -1, 0); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, codec_ctx->width, codec_ctx->height); rect.w = codec_ctx->width; rect.h = codec_ctx->height; ``` 4. 读取视频流、解码和渲染。 ```c AVPacket packet; int frame_finished; AVFrame* frame = av_frame_alloc(); while(av_read_frame(format_ctx, &packet) >= 0) { if(packet.stream_index == video_stream_index) { if(avcodec_send_packet(codec_ctx, &packet) == 0) { while(avcodec_receive_frame(codec_ctx, frame) == 0) { sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); sws_scale(sws_ctx, (const uint8_t * const*)frame->data, frame->linesize, 0, codec_ctx->height, (uint8_t * const *)output_buffer, &pitch); SDL_UpdateYUVTexture(texture, NULL, output_buffer, codec_ctx->width, output_buffer+codec_ctx->width*codec_ctx->height, codec_ctx->width/2, output_buffer+codec_ctx->width*codec_ctx->height*5/4, codec_ctx->width/2); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); sws_freeContext(sws_ctx); } } } av_packet_unref(&packet); } ``` 5. 释放资源。 ```c avformat_close_input(&format_ctx); avcodec_free_context(&codec_ctx); SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); ``` 总体上来说,使用SDL2播放视频需要对FFmpeg库有一定的了解,掌握基本的视频解码、像素转换和纹理渲染等技巧。对于初学者来说,需要仔细研究相关文档和示例程序,并多做实践。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

破浪征程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值