关于如何在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. Origin的图片导出问题

    很多会议投稿都会要求提交的pdf文件用的是type1字体,因为type1字体是矢量字体,无论怎么放大缩小都不会失真.一旦pdf里嵌入了其他非矢量字体,例如type3字体,就会通不过测试,一个典型的例子 ...

  2. Javascript提升阶段学习

    JavaScript1:javascript简介 JavaScript是一种脚本语言,能实现网页内容的交互显示,当用户在客户端显示该网页时,浏览器就会执行JavaScript程序,用户通过交互的操作来 ...

  3. MyEclipse使用总结——MyEclipse文件查找技巧 ctrl+shift+R ctrl+H

    一.查找文件 使用快捷键[ctrl+shift+R]弹出弹出文件查找框,如下图所示: 二.查找包含某个字符串的文件 使用快捷键[ctrl+H]在弹出对话框中选File Search选项,然后在第一个文 ...

  4. Python之线程池

    版本一: #!/usr/bin/env python # -*- coding:utf-8 -*- import Queue import threading class ThreadPool(obj ...

  5. Android兼容包multidex的开发和构建方法

    在Android开发中,函数方法超过65k限制后,我们就常常会用到multidex分包解决,但是multidex的配置,对系统apk的构建.签名.打包复杂性大大的增加,严重的降低了构建效率.那这个问题 ...

  6. Android图片处理-相机、相处简单调用

    安卓开发中,常常需要使用到手机相机拍照.或者相册上传头像等等.通过使用Intent,我们很方便地获得相机.相册里面的图片: 1.相机调用,通过设置File文件路径和文件名,可以将拍照得到的图片保存下来 ...

  7. vs2012 MSDN帮助文档离线包下载安装方法

    vs2012安装文件 自带的 MSDN帮助文档不全, 需要自己手动添加需要的离线文档包, 具体方法如下 1. 打开 vs2012 2. 按 ctrl + alt + F1 打开帮助文档管理器 3. 在 ...

  8. Android中的内部类引起的内存泄露

    引子 什么是内部类?什么是内存泄露?为什么Android的内部类容易引起内存泄露?如何解决? 什么是内部类? 什么是内部类?什么又是外部类.匿名类.局部类.顶层类.嵌套类?大家可以参考我这篇文章 ,再 ...

  9. Chkdsk scan needed on volume

    After we extended the volume in storage array, in Failover cluster, it shows the volume is of 30.0 T ...

  10. 用Task代替TheadPool

    TheadPool的问题 不支持线程的取消.完成.失败通知等交互性操作 不支持线程执行先后次序 using System; using System.Diagnostics; using System ...