关于如何在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解码声音的更多相关文章

  1. [原]如何在Android用FFmpeg+SDL2.0解码图像线程

    关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,关于如何在Android使用FFmpeg+SDL2.0解码声 ...

  2. [原]如何在Android用FFmpeg+SDL2.0解码显示图像

    如何在Android上使用FFmpeg解码图像参考文章[原]如何在Android用FFmpeg解码图像 ,如何在Android上使用SDL2.0来显示图像参考[原]零基础学习SDL开发之在Androi ...

  3. [原]如何在Android用FFmpeg+SDL2.0之同步音频

    同步音频的原理可以参考:http://dranger.com/ffmpeg/tutorial05.html  本文是在 [原]如何在Android用FFmpeg+SDL2.0之同步视频 的基础上面继续 ...

  4. [原]如何在Android用FFmpeg+SDL2.0之同步视频

    关于视频同步的原理可以参考http://dranger.com/ffmpeg/tutorial05.html 和 [原]基础学习视频解码之同步视频 这两篇文章,本文是在这两篇的基础上移植到了Andro ...

  5. [原]如何在Android用FFmpeg解码图像

    前一篇[原]如何用Android NDK编译FFmpeg 我们知道了如何使用NDK来编译Android平台下使用的FFmpeg动态库.这篇文章我们就可以使用Android下的JNI来调用FFMpeg进 ...

  6. QT+FFMPEG+SDL2.0实现视频播放

    开发环境:MinGW+QT5.9+FFMPEG20190212+SDL2.0.9 一.开发环境搭建 (1)下载工具 在https://ffmpeg.zeranoe.com/builds/下载对应版本. ...

  7. [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP图

    关于如何移植SDL2.0到安卓上面来参考我的上一篇文章:[原]零基础学习SDL开发之移植SDL2.0到Android 在一篇文章我们主要使用SDL2.0来加载一张BMP图来渲染显示. 博主的开发环境: ...

  8. [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP叠加图

    关于如何移植在android上使用SDL,可以参考[原]零基础学习SDL开发之移植SDL2.0到Android 和 [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP图 . 在一篇 ...

  9. [原]零基础学习SDL开发之在Android使用SDL2.0渲染PNG图片

    在上一篇文章我们知道了如何在android使用SDL2.0来渲染显示一张bmp图,但是如果是一张png或者一张jpg的图,那么还能显示成功么?答案是否定的 我们需要移植SDL_image库来支持除bm ...

随机推荐

  1. C#与数据库访问技术总结(六)之Command对象创建SQl语句代码示例

    Command对象创建SQl语句代码示例 说明:前面介绍了 Command 对象的方法和一些属性,回顾一下 Command对象主要用来执行SQL语句.利用Command对象,可以查询数据和修改数据. ...

  2. [转]HTTP协议及其请求头分析

      众所周知,Internet的基本协议是TCP/IP协议,目前广泛采用的FTP.Archie Gopher等是建立在TCP/IP协议之上的应用层协议,不同的协议对应着不同的应用.  WWW服务器使用 ...

  3. asp.net datatable 导出为 txt

    如下是导出到TXT的方法 public static void ToTxt(DataTable dv, string FileName) { System.IO.StringWriter sw = n ...

  4. Atitit.识别损坏的图像

    Atitit.识别损坏的图像 判断jpg图像损坏原理.读取gray line perc ent Png图片送货原理,直接回报EOFException /atiplat_cms/src/com/atti ...

  5. atitit.GMT UTC Catitit.GMT UTC CST DST CET 星期 月份 节日 时间的不同本质and起源

    atitit.GMT UTC Catitit.GMT UTC CST DST CET 星期 月份 节日 时间的不同本质and起源 1. GMT(Greenwich Mean Time)是格林尼治平时 ...

  6. Leetcode 125 Valid Palindrome 字符串处理

    题意:判断字符串是否是回文字符串 先将所有的字母和数字字符保留,并将大写字母转化成小写字母,然后将字符串倒置,比较前后两个字符串是否相同. 该题最好的解法可以模仿 Leetcode 345 Rever ...

  7. 重学JAVA基础(八):锁的基本知识

    1.线程状态 如上图,当我们新建一个线程,并start后,其实不一定会马上执行,因为只有操作系统调度了我们的线程,才能真正进行执行,而操作系统也随时可以运行其他线程,这时线程又回到可运行状态.这个过程 ...

  8. 关闭Windows Update更新驱动程序

    关于Win10的更新配置,特别是自动更新驱动程序,经常会导致驱动安装错误而无法开机的问题. 此时只好开机时按F8进入高级模式恢复最后一次正确配置,或者在安全模式删除错误的驱动程序. 关于Win10的更 ...

  9. ListView、PullToRefreshListView滑动加载可见item

    最近用的了PullToRefreshListView框架,也在listView中加载图片,对于滑动加载可见item,网上找了一些相关文档,但都不太合适,如:http://blog.csdn.net/z ...

  10. Accessorizer的使用说明!

    工欲善其事,必先利其器 还是这句老话!但是老话藏真金! 今天我们来说说Accessorizer这个软件吧!!Accessorizer这个软件真的很不错!!但是我没有见到程序员使用它!! Accesso ...