[原]如何在Android用FFmpeg+SDL2.0解码声音
关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,本文是基于上述文章和[原]零基础学习视频解码之解码声音 来移植到Android设备上的,也是参考http://dranger.com/ffmpeg/tutorial03.html来编写的。只不过dranger的文章比较老了,FFmpeg很多接口变了,因此采用了最新的接口替换,并且采用FFmpeg+SDL2.0来做解码和输出。
博主的开发环境:Ubuntu 14.04 64位,Eclipse+CDT+ADT+NDK。
在文章开始之前假定你已经知道如何使用NDK编译FFmpeg,以及知道如何移植SDL2.0到Android平台上来并且知道如何解码显示图像了,如有不明白的可以参考文章开头的两篇文章。
工程中的目录结构和[原]如何在Android用FFmpeg+SDL2.0解码显示图像 一样,只是在其基础上继续添加功能而已。解码声音的原理可以参考http://dranger.com/ffmpeg/tutorial03.html
下面直接给出解码声音的代码内容:
/*
* SDL_Lesson.c
*
* Created on: Aug 12, 2014
* Author: clarck
*/
#include <jni.h>
#include <android/native_window_jni.h>
#include "SDL.h"
#include "SDL_thread.h"
#include "SDL_events.h"
#include "../include/logger.h"
#include "../ffmpeg/include/libavcodec/avcodec.h"
#include "../ffmpeg/include/libavformat/avformat.h"
#include "../ffmpeg/include/libavutil/pixfmt.h"
#include "../ffmpeg/include/libswscale/swscale.h" #define SDL_AUDIO_BUFFER_SIZE 1024
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio typedef struct PacketQueue {
AVPacketList *first_pkt, *last_pkt;
int nb_packets;
int size;
SDL_mutex *mutex;
SDL_cond *cond;
} PacketQueue; PacketQueue audioq; int quit = ; void packet_queue_init(PacketQueue *q) {
memset(q, , 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) < ) {
return -;
}
pkt1 = av_malloc(sizeof(AVPacketList));
if (!pkt1)
return -;
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 ;
} static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
AVPacketList *pkt1;
int ret; SDL_LockMutex(q->mutex); for (;;) { if (quit) {
ret = -;
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);
ret = ;
break;
} else if (!block) {
ret = ;
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) { static AVPacket pkt;
static uint8_t *audio_pkt_data = NULL;
static int audio_pkt_size = ;
static AVFrame frame; int len1, data_size = ; for (;;) {
while (audio_pkt_size > ) {
int got_frame = ;
len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
if (len1 < ) {
/* if error, skip frame */
audio_pkt_size = ;
break;
}
audio_pkt_data += len1;
audio_pkt_size -= len1;
if (got_frame) {
data_size = frame.linesize[];
/*
data_size = av_samples_get_buffer_size(NULL,
aCodecCtx->channels, frame.nb_samples,
aCodecCtx->sample_fmt, 1);
*/
memcpy(audio_buf, frame.data[], data_size);
}
if (data_size <= ) {
/* No data yet, get more frames */
continue;
}
/* We have data, return it and come back for more later */
return data_size;
}
if (pkt.data)
av_free_packet(&pkt); if (quit) {
return -;
} if (packet_queue_get(&audioq, &pkt, ) < ) {
return -;
}
audio_pkt_data = pkt.data;
audio_pkt_size = pkt.size;
} return ;
} void audio_callback(void *userdata, Uint8 *stream, int len) { AVCodecContext *aCodecCtx = (AVCodecContext *) userdata;
int len1, audio_size; static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * ) / ];
static unsigned int audio_buf_size = ;
static unsigned int audio_buf_index = ; while (len > ) {
if (audio_buf_index >= audio_buf_size) {
/* We have already sent all our data; get more */
audio_size = audio_decode_frame(aCodecCtx, audio_buf,
sizeof(audio_buf));
if (audio_size < ) {
/* If error, output silence */
audio_buf_size = ; // arbitrary?
memset(audio_buf, , audio_buf_size);
} else {
audio_buf_size = audio_size;
}
audio_buf_index = ;
}
len1 = audio_buf_size - audio_buf_index;
if (len1 > len)
len1 = len;
memcpy(stream, (uint8_t *) audio_buf + audio_buf_index, len1);
len -= len1;
stream += len1;
audio_buf_index += len1;
}
} int main(int argc, char *argv[]) {
char *file_path = argv[];
LOGI("file_path:%s", file_path); AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame, *pFrameYUV;
AVPacket *packet;
uint8_t *out_buffer; AVCodecContext *aCodecCtx;
AVCodec *aCodec; SDL_Texture *bmp = NULL;
SDL_Window *screen = NULL;
SDL_Rect rect;
SDL_Event event; SDL_AudioSpec wanted_spec, spec; static struct SwsContext *img_convert_ctx; int videoStream, audioStream, i, numBytes;
int ret, got_picture; av_register_all();
pFormatCtx = avformat_alloc_context(); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
LOGE("Could not initialize SDL - %s. \n", SDL_GetError());
exit();
} if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != ) {
LOGE("can't open the file. \n");
return -;
} if (avformat_find_stream_info(pFormatCtx, NULL) < ) {
LOGE("Could't find stream infomation.\n");
return -;
} videoStream = ;
audioStream = -;
for (i = ; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
&& audioStream < ) {
audioStream = i;
}
} LOGI("videoStream:%d", videoStream);
if (videoStream == -) {
LOGE("Didn't find a video stream.\n");
return -;
} if (audioStream == -) {
LOGE("Didn't find a audio stream.\n");
return -;
} aCodecCtx = pFormatCtx->streams[audioStream]->codec;
// Set audio settings from codec info
wanted_spec.freq = aCodecCtx->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = aCodecCtx->channels;
wanted_spec.silence = ;
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = aCodecCtx; if (SDL_OpenAudio(&wanted_spec, &spec) < ) {
LOGE("SDL_OpenAudio: %s\n", SDL_GetError());
return -;
}
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if (!aCodec) {
LOGE("Unsupported codec!\n");
return -;
}
avcodec_open2(aCodecCtx, aCodec, NULL); // audio_st = pFormatCtx->streams[index]
packet_queue_init(&audioq);
SDL_PauseAudio(); pCodecCtx = pFormatCtx->streams[videoStream]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) {
LOGE("Codec not found.\n");
return -;
} if (avcodec_open2(pCodecCtx, pCodec, NULL) < ) {
LOGE("Could not open codec.\n");
return -;
} pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc(); //---------------------------init sdl---------------------------//
screen = SDL_CreateWindow("My Player Window", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width, pCodecCtx->height,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL); SDL_Renderer *renderer = SDL_CreateRenderer(screen, -, ); bmp = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height); //-------------------------------------------------------------// numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
pCodecCtx->height);
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P,
pCodecCtx->width, pCodecCtx->height); rect.x = ;
rect.y = ;
rect.w = pCodecCtx->width;
rect.h = pCodecCtx->height; int y_size = pCodecCtx->width * pCodecCtx->height; packet = (AVPacket *) malloc(sizeof(AVPacket));
av_new_packet(packet, y_size); av_dump_format(pFormatCtx, , file_path, ); while (av_read_frame(pFormatCtx, packet) >= ) {
if (packet->stream_index == videoStream) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,
packet); if (ret < ) {
LOGE("decode error.\n");
return -;
} LOGI("got_picture:%d", got_picture);
if (got_picture) {
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, , pCodecCtx->height, pFrameYUV->data,
pFrameYUV->linesize);
sws_freeContext(img_convert_ctx);
////iPitch 计算yuv一行数据占的字节数
SDL_UpdateYUVTexture(bmp, &rect,
pFrameYUV->data[], pFrameYUV->linesize[],
pFrameYUV->data[], pFrameYUV->linesize[],
pFrameYUV->data[], pFrameYUV->linesize[]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, bmp, &rect, &rect);
SDL_RenderPresent(renderer);
}
av_free_packet(packet);
} else if (packet->stream_index == audioStream) {
packet_queue_put(&audioq, packet);
} else {
av_free_packet(packet);
} SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
SDL_Quit();
exit();
break;
default:
break;
}
}
SDL_DestroyTexture(bmp); av_free(out_buffer);
av_free(pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx); return ;
}
这里看到的图像比较快以及声音和图像不同步,且声音噪音很大。原因是没有做声音和图像的同步处理,这里不同步也很正常。关于噪音很大,是有一个小细节的处理不到位造成的。
[原]如何在Android用FFmpeg+SDL2.0解码声音的更多相关文章
- [原]如何在Android用FFmpeg+SDL2.0解码图像线程
关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,关于如何在Android使用FFmpeg+SDL2.0解码声 ...
- [原]如何在Android用FFmpeg+SDL2.0解码显示图像
如何在Android上使用FFmpeg解码图像参考文章[原]如何在Android用FFmpeg解码图像 ,如何在Android上使用SDL2.0来显示图像参考[原]零基础学习SDL开发之在Androi ...
- [原]如何在Android用FFmpeg+SDL2.0之同步音频
同步音频的原理可以参考:http://dranger.com/ffmpeg/tutorial05.html 本文是在 [原]如何在Android用FFmpeg+SDL2.0之同步视频 的基础上面继续 ...
- [原]如何在Android用FFmpeg+SDL2.0之同步视频
关于视频同步的原理可以参考http://dranger.com/ffmpeg/tutorial05.html 和 [原]基础学习视频解码之同步视频 这两篇文章,本文是在这两篇的基础上移植到了Andro ...
- [原]如何在Android用FFmpeg解码图像
前一篇[原]如何用Android NDK编译FFmpeg 我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库.这篇文章我们就可以使用Android下的JNI来调用FFMpeg进 ...
- QT+FFMPEG+SDL2.0实现视频播放
开发环境:MinGW+QT5.9+FFMPEG20190212+SDL2.0.9 一.开发环境搭建 (1)下载工具 在https://ffmpeg.zeranoe.com/builds/下载对应版本. ...
- [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP图
关于如何移植SDL2.0到安卓上面来参考我的上一篇文章:[原]零基础学习SDL开发之移植SDL2.0到Android 在一篇文章我们主要使用SDL2.0来加载一张BMP图来渲染显示. 博主的开发环境: ...
- [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP叠加图
关于如何移植在android上使用SDL,可以参考[原]零基础学习SDL开发之移植SDL2.0到Android 和 [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP图 . 在一篇 ...
- [原]零基础学习SDL开发之在Android使用SDL2.0渲染PNG图片
在上一篇文章我们知道了如何在android使用SDL2.0来渲染显示一张bmp图,但是如果是一张png或者一张jpg的图,那么还能显示成功么?答案是否定的 我们需要移植SDL_image库来支持除bm ...
随机推荐
- atitit.跨平台gui 概览
atitit.跨平台gui 概览 为什么需要跨平台gui 国际上那些跨平台的GUI程序,除了像Firefox之类的大型项目会重写界面外,中小型的项目基本上都是用GTK+或WxWidgets为多.毕竟要 ...
- atitit.架构设计---方法调用结果使用异常还是返回值
atitit.架构设计---方法调用结果使用异常还是返回值 1. 应该返回BOOL类型还是异常 1 2. 最终会有四种状况,抛出异常.返回特殊值.阻塞.超时 1 3. 异常的优缺点点 1 4. jav ...
- iOS---性能优化
最近采用Instruments 来分析整个应用程序的性能.发现很多有意思的点,以及性能优化和一些分析性能消耗的技巧.小结如下. Instruments使用技巧 关于Instruments官方有一个很有 ...
- JS 基本数据类型
一.undefined 类型 (ECMAScript 3引入undefined类型) 1.它的值只有一个 undefined 2.未初始化的变量 会隐式转换为undeFined类型 var box; ...
- 负margin在布局中的运用
一.左右栏宽度固定,中间栏宽度自适应 <!DOCTYPE html> <html> <head lang="en"> <meta char ...
- android: SQLite使用 SQL 操作数据库
虽然 Android 已经给我们提供了很多非常方便的 API 用于操作数据库,不过总会有一些 人不习惯去使用这些辅助性的方法,而是更加青睐于直接使用 SQL 来操作数据库.这种人 一般都是属于 SQL ...
- 我所理解的JavaScript闭包
目录 一.闭包(Closure) 1.1.什么是闭包? 1.2.为什么要用闭包(作用)? 1.2.1.保护函数内的变量安全. 1.2.2.通过访问外部变量,一个闭包可以暂时保存这些变量的上下文环境,当 ...
- ftp 命令
- 用GO语言开发editplus编辑器插件(附源码)
我要开发的插件功能极为简单,就是对用户选中的内容进行base64编码或解密工作. 其中所涉及的技术部分主要是GO语言程序开发和editplus插件配置的部分,首先我们来看一下GO语言代码的写法,如下: ...
- CSS等高布局
做一些后台项目,和一下带侧边栏项目的时候登高布局很常用,总结了下有几种 1.margin-bottom方法 这里直接介绍我认为的最佳的侧边栏/分栏高度自动相等方法.核心的CSS代码如下(数值不固定): ...