`
CheshireCat
  • 浏览: 9677 次
社区版块
存档分类
最新评论

利用ffmpeg1.0提取音频并播放

阅读更多

 

很久以前,我就想在android做一个复读机程序,用来播放我喜欢的我从网上下的《老友记》,但编一个mp3播放机当然很容易,但其它格式只能借助其它解码工具了,于是在网上找了一堆关于ffmpeg的资料。其它能完成音频播放的工能,欲发几篇文章,一来记录下这几天成果,二是然望能同修们有帮助。

首先的的开发环境是ubuntu,用的是ffmpeg1.0,现在网上很多资料都在很以前比较老的版本,
网上已有很多关于ffmpeg的下载和在lunix底下编译的文章,这里不不多作介绍了,之于利用ndk编译我以后有时间,会说下自己观点和看法。

提取音频程序与解码可能参考 fmpeg-1.0/doc/examples/filtering_audio.c和ffmpeg-1.0/doc/examples/decoding_encoding.c;ffmpeg.c和ffplay太过复杂,对于我这个急于求成的外行来说不太适宜。网上有几篇文章很好比如,
http://ushertechblog.sinaapp.com/post-24.html

上篇文章也是参照一国外博客http://dranger.com/ffmpeg/tutorial03.html ,只是 这晨面用的都是ffmpeg很老的版本,底下这篇用的是最新ffmpeg,

http://blog.chinaunix.net/uid-26009923-id-3384770.html

下下是我参照上面文章写的代码,只改变了只改decode_audo_frame 与audio_callback函数里的一些内容,或许能让感觉代码更清晰点

#include <stdio.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL.h>
#define SDL_AUDIO_BUFFER_SIZE 1024
typedef struct PacketQueue {
	AVPacketList * first_pkt, *last_pkt;
	int nb_packets;
	int size;
	SDL_mutex *mutex;		//mutex主要是用来实现资源的互拆的,跟java里在synchronize关键作用有相似之处
							//,虽然本和序中没有用多线程,但sdl在播放另启了一个线程。
	SDL_cond * cond;
} PacketQueue;
PacketQueue audioq;

void packet_queue_init(PacketQueue *q) {
	memset(q, 0, sizeof(PacketQueue));
	q->mutex = SDL_CreateMutex();
	q->cond = SDL_CreateCond();
}

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

	AVPacketList *pkt1;
	if (av_dup_packet(pkt) < 0)
		return -1;
	pkt1 = av_malloc(sizeof(AVPacketList));
	if (!pkt1)
		return -1;
	pkt1->pkt = *pkt;
	pkt1->next = NULL;

	SDL_LockMutex(q->mutex);

	if (!q->last_pkt)
		q->first_pkt = pkt1;
	else
		q->last_pkt->next = pkt1;
	q->last_pkt = pkt1;
	q->nb_packets++;
	q->size += pkt1->pkt.size;
	SDL_CondSignal(q->cond);

	SDL_UnlockMutex(q->mutex);
	return 0;
}

int quit = 0;
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
	AVPacketList *pkt1;
	int ret;

	SDL_LockMutex(q->mutex);

	for (;;) {

		if (quit) {
			ret = -1;
			break;
		}

		pkt1 = q->first_pkt;
		if (pkt1) {
			q->first_pkt = pkt1->next;
			if (!q->first_pkt)
				q->last_pkt = NULL;
			q->nb_packets--;
			q->size -= pkt1->pkt.size;
			*pkt = pkt1->pkt;
			av_free(pkt1);			//这招我很赞赏,他在取去一个packet后,将上一个paket置空,而在下面的程序就不用再调用av_free操作了
			ret = 1;
			break;
		} else if (!block) {
			ret = 0;
			break;
		} else {
			SDL_CondWait(q->cond, q->mutex);
		}
	}
	SDL_UnlockMutex(q->mutex);
	return ret;
}

//int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size) {
int audio_decode_frame(AVCodecContext *aCodecCtx, AVFrame *frame,
		uint8_t *audio_buf) {
	static AVPacket pkt_temp;
	int len1, data_size, got_frame;
	int new_packet;
	for (;;) {
		while (pkt_temp.size > 0 || (!pkt_temp.data && new_packet)) {
			if (!frame) {
				if (!(frame = avcodec_alloc_frame()))
					return AVERROR(ENOMEM);
			} else {
				avcodec_get_frame_defaults(frame);
			}
			new_packet = 0;

			len1 = avcodec_decode_audio4(aCodecCtx, frame, &got_frame,
					&pkt_temp);
			if (len1 < 0) {
				/* if error, skip frame */
				pkt_temp.size = 0;
				break;
			}
			pkt_temp.data += len1;
			pkt_temp.size -= len1;

			if (got_frame <= 0) /* No data yet, get more frames */
				continue;
			data_size = av_samples_get_buffer_size(NULL, aCodecCtx->channels,
					frame->nb_samples, aCodecCtx->sample_fmt, 1);
			memcpy(audio_buf, frame->data[0], frame->linesize[0]);
			/* We have data, return it and come back for more later */
			return data_size;
		}
		if (quit)
			return -1;

		if ((new_packet = packet_queue_get(&audioq, &pkt_temp, 1)) < 0)
			return -1;

	}
}

void audio_callback(void *userdata, Uint8 *stream, int len) {
	AVCodecContext *aCodecCtx = (AVCodecContext *) userdata;
	//以后变量全定义成static ,确保下次循环,变量不会被初始化
	static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
	static unsigned int audio_buf_remain_size=0;		//记录下audio_buffer剩余数据量
	static unsigned int audio_buf_total_size=0;			//记录下audio_buffer总数据量
	static unsigned int audio_buf_index = 0;

	int read_size; //第次送入*stream中数据的真正长度,理论值是len,但在最后一次操作实际值可能会小于len;

	AVFrame *frame = NULL;


	int flag=0;
	while(len){

		if(audio_buf_index>=audio_buf_total_size){

			audio_buf_remain_size = audio_decode_frame(aCodecCtx, frame, audio_buf);
			  audio_buf_total_size=audio_buf_remain_size;


			audio_buf_index=0;
			if(audio_buf_total_size<0){
						audio_buf_remain_size=audio_buf_total_size = 1024;
						memset(audio_buf, 0, audio_buf_total_size);
						continue;
			}
		}



		read_size=(audio_buf_remain_size > len)? len : audio_buf_remain_size;


		memcpy(stream, (uint8_t *) audio_buf + audio_buf_index, read_size);

		audio_buf_index += read_size;
		audio_buf_remain_size -= read_size;
		stream += read_size;
		len -= read_size;
	}


}


int main(int argc, char *argv[]) {
	AVFormatContext *pFormatCtx;
	int i, videoStream, audioStream;
	AVCodecContext *pCodecCtx, *aCodecCtx;
	AVCodec *pCodec, *aCodec;
	AVFrame *pFrame;
	AVFrame *pFrameRGB;
	AVPacket packet;
	SDL_Event event;
	int frameFinished;
	int numBytes;
	uint8_t *buffer;

	if (argc < 2) {
		printf("Please provide a movie file\n");
		return -1;
	}

	if (SDL_Init(SDL_INIT_AUDIO)) {
		fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
		exit(1);
	}
	av_register_all();

	pFormatCtx = avformat_alloc_context();
	// Open video file
	//if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
	if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL ) != 0)
		return -1; // Couldn't open file

	// Retrieve stream information
	if (avformat_find_stream_info(pFormatCtx, NULL ) < 0)
		return -1; // Couldn't find stream information

	// Dump information about file onto standard error
	av_dump_format(pFormatCtx, 0, argv[1], 0);

	// Find the first video stream
	videoStream = -1;
	audioStream = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++) {
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO
				&& videoStream < 0)
			videoStream = i;
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
				&& audioStream < 0)
			audioStream = i;
	}
	if (videoStream == -1)
		return -1; // Didn't find a video stream
	if (audioStream == -1)
		return -1;
	// Get a pointer to the codec context for the video&audio stream
	pCodecCtx = pFormatCtx->streams[videoStream]->codec;

	aCodecCtx = pFormatCtx->streams[audioStream]->codec;

	SDL_AudioSpec wanted_spec, spec;
	wanted_spec.freq = aCodecCtx->sample_rate;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = aCodecCtx->channels;
	wanted_spec.silence = 0;
	wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
	wanted_spec.callback = audio_callback;
	wanted_spec.userdata = aCodecCtx;

	if (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
		fprintf(stderr, "SDL_OpenAudio:%s\n", SDL_GetError());
		return -1;
	}

	// Find the decoder for the video stream
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL ) {
		fprintf(stderr, "Unsupported codec!\n");
		return -1; // Codec not found
	}
	// Find the decoder for the video stream
	aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
	if (aCodec == NULL ) {
		fprintf(stderr, "Unsupported codec!\n");
		return -1; // Codec not found
	}
	// Open codec
	if (avcodec_open2(aCodecCtx, aCodec, NULL ) < 0)
		return -1; // Could not open codec
	// Open codec
	if (avcodec_open2(pCodecCtx, pCodec, NULL ) < 0)
		return -1; // Could not open codec

	packet_queue_init(&audioq);
	SDL_PauseAudio(0);
	//Set up a screen

	while (av_read_frame(pFormatCtx, &packet) >= 0) {
		// Is this a packet from the video stream?
		if (packet.stream_index == audioStream) {
			packet_queue_put(&audioq, &packet);
		} else {
			av_free_packet(&packet);
		}
		SDL_PollEvent(&event);
		switch (event.type) {
		case SDL_QUIT:
			quit = 1;
			SDL_Quit();
			exit(0);
			break;
		default:
			break;
		}

	}
	// Free the packet that was allocated by av_read_frame
	av_free_packet(&packet);

	// Free the RGB image

	for (;;) {

		getchar();							//这个是用来阻塞主线程,也可以用来接收键盘命令,比如暂停播放之类的
		break;
	}
	// Close the codec
	avcodec_close(pCodecCtx);
	avcodec_close(aCodecCtx);

	// Close the video file
	avformat_close_input(&pFormatCtx);

	return 0;
}



 

分享到:
评论

相关推荐

    最新版本的ffmpeg1.0

    最新版本的FFmpeg 1.0不仅在稳定性上有所提升,还引入了更多的编码器、解码器支持以及优化的性能。 FFmpeg的核心功能包括: 1. 视频转换:FFmpeg能够将各种不同格式的视频文件转换为其他格式,如MP4、AVI、FLV等,...

    ffmpeg的jar包和jffmpeg的 jar包.zip

    FFmpeg 是一个强大的开源命令行工具,用于处理音频和视频文件。它支持各种编码、解码、转换、流媒体和过滤任务。在Java环境中,如果你需要在应用中集成FFmpeg的功能,可以使用JAVE(Java Audio Video Encoder)和...

    FFmpeg_audio_video_demo_android

    它还提供了强大的命令行工具,用于执行各种任务,如提取音频、裁剪视频、合并音视频等。在Android上,FFmpeg通常通过JNI(Java Native Interface)与Java代码交互,这样可以利用C/C++的性能优势处理多媒体数据。 ...

    ffmpeg支持dxva2.zip

    1. **FFmpeg**:这是一个跨平台的音频和视频处理工具集,包含了一系列的库和命令行工具,用于处理音视频文件的各种操作,如转换格式、提取音频、裁剪视频等。 2. **DXVA2**:DirectX Video Acceleration 2.0是微软...

    FFMpeg实现完整播放器,可直接供工程或者项目调用做视频回放,功能强大

    FFmpeg是一款开源的跨平台多媒体处理工具,广泛用于视频、音频的编码、解码、转换、流媒体等任务。在本项目中,FFmpeg被封装成了一个完整的播放器,适用于工程或项目中直接调用,以实现视频回放功能。这个播放器的...

    MPlayer-1.0rc2.tar.bz2及简单安装编译过程

    MPlayer是一款强大的开源媒体播放器,它支持众多的视频、音频格式和编码,可以在各种操作系统上运行,包括Linux、Windows和Mac OS X等。MPlayer-1.0rc2是该软件的一个较早版本,发布时处于候选发布状态,意味着它是...

    M3U8下载器(请自行检查系统里是否有net4.6)V1.0.0.0.5

    在这个下载器中,FFmpeg的作用是解析M3U8文件,提取出其中的TS(Transport Stream)分片,并将这些分片逐个下载到本地,最后再进行合并,形成完整的视频文件。 M3U8文件通常包含一系列TS分片的URL,这些分片按顺序...

    TS流提取ES流的代码

    这种操作通常涉及到对TS包头的解析,找出包含特定服务(如视频或音频)的包,并将这些包的数据部分重组为ES流。 TS包的结构一般包括一个188字节的包头,其中包含PID(Packet Identifier)、PTS/DTS(Presentation ...

    M3U8下载器(自动解密+合并:只需键入index.m3u8地址)

    M3U8是一种基于HTTP Live Streaming (HLS)协议的媒体播放列表格式,主要用于在互联网上分段传输音频和视频内容。这个下载器的独特之处在于它集成了ffmpeg组件,这是一个强大的开源跨平台多媒体处理框架。 描述中...

    node-pcm:从媒体文件中提取PCM数据并将其提取到node.js中

    节点-pcm 获取包含音频流的文件,并使用ffmpeg异步返回原始PCM数据。安装 npm install pcm用法 var pcm = require('pcm');var min = 1.0;var max = -1.0;pcm.getPcmData('test.mp3', { stereo: true, sampleRate: ...

    使用python实现语音文件的特征提取方法

    然而,在训练这些复杂的模型之前,最重要的一环是对原始的语音信号进行处理并提取出有用的特征。 本文将详细介绍如何使用Python来实现语音文件的特征提取,包括MP3文件转换为WAV格式、读取WAV文件并对语音进行采样...

    音视频转码,合成用到的jar包

    通过这个库,你可以进行复杂的操作,比如提取音频轨道,裁剪视频,改变编码格式,添加水印,甚至进行实时流媒体转码。 以上三个jar包结合使用,可以构建出一个全面的音视频处理系统。开发者可以通过JAVE进行转码...

    sacd_extract-gui

    2. sacd_extract-gui读取并解析这些文件,提取出DSD音频流。 3. 使用内置的转换算法,将DSD音频流转化为dff或dsf格式。 4. 用户可以选择输出路径和文件命名规则,然后启动转换过程。 5. 转换完成后,用户可以在指定...

    Linux获取视频代码

    1. **FFmpeg**: FFmpeg是一款强大的开源命令行工具,用于处理多媒体文件,包括视频、音频、图像等。它支持多种格式的编码、解码、转换、提取帧等功能。例如,你可以使用`ffmpeg -i input.mp4 output.jpg`命令从视频...

    DSFTREG

    - ffdshow是一套基于FFmpeg的视频和音频解码器,它为多种媒体播放器提供广泛的编码支持。 - 更新ffdshow意味着可以支持更多的媒体格式,并且提高了播放质量和效率。 **修复寻址时可能会崩溃的问题** - **知识点:...

    获取MP3中的ID3V1、ID3V2信息.rar_MP3 ID3_id3_id3v2_mp3 tag_mp3 信息

    在C程序"获取MP3中的ID3V1、ID3V2信息.c"中,可能包含了这样的实现,包括打开MP3文件、解析ID3标签、提取元数据信息并显示给用户的过程。 在处理ID3标签时,需要注意以下几点: 1. 文件指针定位:由于ID3标签通常...

Global site tag (gtag.js) - Google Analytics