关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,关于如何在Android使用FFmpeg+SDL2.0解码声音参考[原]如何在Android用FFmpeg+SDL2.0解码声音。但是该文章有一个问题,就是解码出来的声音有很大的噪音,基本无法听清,这是由于对于声音的处理有问题。故本文参考ffmpeg-sdl音频播放分析声音解码的处理,解码出来的声音就正常了。

博主的开发环境:Ubuntu 14.04 64位,Eclipse+CDT+ADT+NDK。

在文章开始之前假定你已经知道如何使用NDK编译FFmpeg,以及知道如何移植SDL2.0到Android平台上来并且知道如何解码显示图像和声音了。

我们初步了解如何解码视频图像和视频声音。但是这些都是初步简单的解码出来而已,我们的主要功能是处理非常多:它是通过事件循环中运行,读取数据包,并在视频解码。所以,我们要做的就是拆分这些功能:我们将有一个线程,该线程将负责数据包进行解码;这些数据包将被添加到该队列中,并通过相应的音频和视频解码线程读取。

音频线:我们在[原]如何在Android用FFmpeg+SDL2.0解码声音这篇文章里面有了一个初步的音频线模型,在本文我们继续去完善这个;

视频线:频线会相对比较麻烦一些,因为我们要自己显示自己的视频画面。我们将实际实现的代码添加到主循环。我们的想法是对视频进行解码,保存生成到一个队列中,然后创建一个自定义刷新事件(FF_REFRESH_EVENT),我们添加它到事件系统中,那么当我们的事件循环看到这种情况,它会显示在队列的下一帧中,这样一边解码一边显示。

工程中的目录结构和[原]如何在Android用FFmpeg+SDL2.0解码声音 一样,只是在其基础上继续添加功能。

一、创建一个VideoPicture结构体用来保存解码出来的图像。

二、添加数据队列的初始化、添加以及读取的函数。

三、audio_decode_frame():解码音频

四、audio_callback(): 回调函数,向SDL缓冲区填充数据

五、创建视频刷新相关的函数:

schedule_refresh():它主要的作用是告诉系统指定的毫秒数后推FF_REFRESH_EVENT。当我们看到它在事件队列时,将依次调用视频刷新功能。

六、添加视频显示函数:

因为我们的屏幕可以是任意大小(我们设定我们为640×480,并有一些方法来设置它,所以它是由用户调整大小),我们需要动态地计算出我们有多大的矩形。因此,首先我们需要弄清楚我们的电影的显示比例,这仅仅是宽度除以身高。某些编解码器将有一个奇怪的样本纵横比,这就是一个像素,或样品的宽度/高度。因为在我们的编解码器的上下文中的高度和宽度值以像素为单位测量,实际的宽高比等于宽高比数倍的样品长宽比。一些编解码器将显示0-5的宽高比,这表示每个像素仅仅是大小1x1的。然后,我们扩展了电影,以适应在我们的屏幕上。

七、分配显示输出内存空间:

使用队列中,我们有两个指针 - 写入索引和阅读索引。我们还跟踪实际的照片有多少是在缓冲区中。要写入队列中,我们将首先等待我们的缓冲清除,所以我们足够的空间来存储我们VideoPicture。然后我们检查,看看是否已经分配了覆盖在我们的写作索引。如果没有,我们就必须分配一定的空间。如果窗口的大小发生了变化, 我们也要重新分配缓冲区。

八、解码线程,将解码器,建立音频线,保存重要信息到数据结构中。

九、编写Main函数用来调用解码线程。

/*
* 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"
#include "../ffmpeg/include/libswresample/swresample.h" #define SDL_AUDIO_BUFFER_SIZE 1024 #define MAX_AUDIO_SIZE (5 * 16 * 1024)
#define MAX_VIDEO_SIZE (5 * 256 * 1024) #define FF_ALLOC_EVENT (SDL_USEREVENT)
#define FF_REFRESH_EVENT (SDL_USEREVENT + 1)
#define FF_QUIT_EVENT (SDL_USEREVENT + 2) #define VIDEO_PICTURE_QUEUE_SIZE 1
#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; typedef struct VideoPicture {
SDL_Window *screen;
SDL_Renderer *renderer;
SDL_Texture *bmp; AVFrame* rawdata;
int width, height; /*source height & width*/
int allocated;
} VideoPicture; typedef struct VideoState {
char filename[1024];
AVFormatContext *ic;
int videoStream, audioStream;
AVStream *audio_st;
AVFrame *audio_frame;
PacketQueue audioq;
unsigned int audio_buf_size;
unsigned int audio_buf_index;
AVPacket audio_pkt;
uint8_t *audio_pkt_data;
int audio_pkt_size;
uint8_t *audio_buf;
DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
enum AVSampleFormat audio_src_fmt;
enum AVSampleFormat audio_tgt_fmt;
int audio_src_channels;
int audio_tgt_channels;
int64_t audio_src_channel_layout;
int64_t audio_tgt_channel_layout;
int audio_src_freq;
int audio_tgt_freq;
struct SwrContext *swr_ctx; AVStream *video_st;
PacketQueue videoq; VideoPicture pictq[VIDEO_PICTURE_QUEUE_SIZE];
int pictq_size, pictq_rindex, pictq_windex;
SDL_mutex *pictq_mutex;
SDL_cond *pictq_cond; SDL_Thread *parse_tid;
SDL_Thread *audio_tid;
SDL_Thread *video_tid; AVIOContext *io_ctx;
struct SwsContext *sws_ctx; int quit;
} VideoState; VideoState *global_video_state; 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; pkt1 = (AVPacketList *) 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;
} static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) {
AVPacketList *pkt1;
int ret; SDL_LockMutex(q->mutex); for (;;) {
if (global_video_state->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);
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(VideoState *is) {
int len1, len2, decoded_data_size;
AVPacket *pkt = &is->audio_pkt;
int got_frame = 0;
int64_t dec_channel_layout;
int wanted_nb_samples, resampled_data_size; for (;;) {
while (is->audio_pkt_size > 0) {
if (!is->audio_frame) {
if (!(is->audio_frame = avcodec_alloc_frame())) {
return AVERROR(ENOMEM);
}
} else
avcodec_get_frame_defaults(is->audio_frame); len1 = avcodec_decode_audio4(is->audio_st->codec, is->audio_frame,
&got_frame, pkt);
if (len1 < 0) {
// error, skip the frame
is->audio_pkt_size = 0;
break;
} is->audio_pkt_data += len1;
is->audio_pkt_size -= len1; if (!got_frame)
continue; /* 计算解码出来的桢需要的缓冲大小 */
decoded_data_size = av_samples_get_buffer_size(NULL,
is->audio_frame->channels, is->audio_frame->nb_samples,
is->audio_frame->format, 1); dec_channel_layout =
(is->audio_frame->channel_layout
&& is->audio_frame->channels
== av_get_channel_layout_nb_channels(
is->audio_frame->channel_layout)) ?
is->audio_frame->channel_layout :
av_get_default_channel_layout(
is->audio_frame->channels); wanted_nb_samples = is->audio_frame->nb_samples; if (is->audio_frame->format != is->audio_src_fmt
|| dec_channel_layout != is->audio_src_channel_layout
|| is->audio_frame->sample_rate != is->audio_src_freq
|| (wanted_nb_samples != is->audio_frame->nb_samples
&& !is->swr_ctx)) {
if (is->swr_ctx)
swr_free(&is->swr_ctx);
is->swr_ctx = swr_alloc_set_opts(NULL,
is->audio_tgt_channel_layout, is->audio_tgt_fmt,
is->audio_tgt_freq, dec_channel_layout,
is->audio_frame->format, is->audio_frame->sample_rate,
0, NULL);
if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) {
fprintf(stderr, "swr_init() failed\n");
break;
}
is->audio_src_channel_layout = dec_channel_layout;
is->audio_src_channels = is->audio_st->codec->channels;
is->audio_src_freq = is->audio_st->codec->sample_rate;
is->audio_src_fmt = is->audio_st->codec->sample_fmt;
} /* 这里我们可以对采样数进行调整,增加或者减少,一般可以用来做声画同步 */
if (is->swr_ctx) {
const uint8_t **in =
(const uint8_t **) is->audio_frame->extended_data;
uint8_t *out[] = { is->audio_buf2 };
if (wanted_nb_samples != is->audio_frame->nb_samples) {
if (swr_set_compensation(is->swr_ctx,
(wanted_nb_samples - is->audio_frame->nb_samples)
* is->audio_tgt_freq
/ is->audio_frame->sample_rate,
wanted_nb_samples * is->audio_tgt_freq
/ is->audio_frame->sample_rate) < 0) {
fprintf(stderr, "swr_set_compensation() failed\n");
break;
}
} len2 = swr_convert(is->swr_ctx, out,
sizeof(is->audio_buf2) / is->audio_tgt_channels
/ av_get_bytes_per_sample(is->audio_tgt_fmt),
in, is->audio_frame->nb_samples);
if (len2 < 0) {
fprintf(stderr, "swr_convert() failed\n");
break;
}
if (len2
== sizeof(is->audio_buf2) / is->audio_tgt_channels
/ av_get_bytes_per_sample(is->audio_tgt_fmt)) {
fprintf(stderr,
"warning: audio buffer is probably too small\n");
swr_init(is->swr_ctx);
}
is->audio_buf = is->audio_buf2;
resampled_data_size = len2 * is->audio_tgt_channels
* av_get_bytes_per_sample(is->audio_tgt_fmt);
} else {
resampled_data_size = decoded_data_size;
is->audio_buf = is->audio_frame->data[0];
}
// We have data, return it and come back for more later
return resampled_data_size;
} if (pkt->data)
av_free_packet(pkt);
memset(pkt, 0, sizeof(*pkt));
if (is->quit)
return -1;
if (packet_queue_get(&is->audioq, pkt, 1) < 0)
return -1; is->audio_pkt_data = pkt->data;
is->audio_pkt_size = pkt->size;
} return 0;
} void audio_callback(void *userdata, Uint8 *stream, int len) {
VideoState *is = (VideoState *) userdata;
int len1, audio_data_size;
/* len是由SDL传入的SDL缓冲区的大小,如果这个缓冲未满,我们就一直往里填充数据 */
while (len > 0) {
/* audio_buf_index 和 audio_buf_size 标示我们自己用来放置解码出来的数据的缓冲区,*/
/* 这些数据待copy到SDL缓冲区, 当audio_buf_index >= audio_buf_size的时候意味着我*/
/* 们的缓冲为空,没有数据可供copy,这时候需要调用audio_decode_frame来解码出更
/* 多的桢数据 */ if (is->audio_buf_index >= is->audio_buf_size) {
audio_data_size = audio_decode_frame(is);
/* audio_data_size < 0 标示没能解码出数据,我们默认播放静音 */
if (audio_data_size < 0) {
/* silence */
is->audio_buf_size = 1024;
/* 清零,静音 */
memset(is->audio_buf, 0, is->audio_buf_size);
} else {
is->audio_buf_size = audio_data_size;
}
is->audio_buf_index = 0;
}
/* 查看stream可用空间,决定一次copy多少数据,剩下的下次继续copy */
len1 = is->audio_buf_size - is->audio_buf_index;
if (len1 > len) {
len1 = len;
} memcpy(stream, (uint8_t *) is->audio_buf + is->audio_buf_index, len1);
len -= len1;
stream += len1;
is->audio_buf_index += len1;
}
} static Uint32 sdl_refresh_timer_cb(Uint32 interval, void *opaque) {
SDL_Event event;
event.type = FF_REFRESH_EVENT;
event.user.data1 = opaque;
SDL_PushEvent(&event);
return 0;
} static void schedule_refresh(VideoState *is, int delay) {
SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
} int decode_interrupt_cb(void *opaque) {
return (global_video_state && global_video_state->quit);
} void video_display(VideoState *is) {
SDL_Rect rect;
VideoPicture *vp;
float aspect_ratio; vp = &is->pictq[is->pictq_rindex];
if (vp->bmp) {
if (is->video_st->codec->sample_aspect_ratio.num == 0) {
aspect_ratio = 0;
} else {
aspect_ratio = av_q2d(is->video_st->codec->sample_aspect_ratio)
* is->video_st->codec->width / is->video_st->codec->height;
} if (aspect_ratio <= 0.0) {
aspect_ratio = (float) is->video_st->codec->width
/ (float) is->video_st->codec->height;
} rect.x = 0;
rect.y = 0;
rect.w = vp->width;
rect.h = vp->height; SDL_UpdateYUVTexture(vp->bmp, &rect, vp->rawdata->data[0],
vp->rawdata->linesize[0], vp->rawdata->data[1],
vp->rawdata->linesize[1], vp->rawdata->data[2],
vp->rawdata->linesize[2]); SDL_RenderClear(vp->renderer);
SDL_RenderCopy(vp->renderer, vp->bmp, &rect, &rect);
SDL_RenderPresent(vp->renderer);
}
} void video_refresh_timer(void *userdata) {
VideoState *is = (VideoState *) userdata;
//VideoPicture *vp;
if (is->video_st) {
if (is->pictq_size == 0) {
schedule_refresh(is, 1);
} else {
//vp = &is->pictq[is->pictq_rindex];
/* Now, normally here goes a ton of code
about timing, etc. we're just going to
guess at a delay for now. You can
increase and decrease this value and hard code
the timing - but I don't suggest that ;)
We'll learn how to do it for real later.
*/
schedule_refresh(is, 80); /* show the picture! */
video_display(is); /* update queue for next picture! */
if (++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) {
is->pictq_rindex = 0;
}
SDL_LockMutex(is->pictq_mutex);
is->pictq_size--;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
}
} else {
schedule_refresh(is, 100);
}
} void alloc_picture(void *userdata) {
VideoState *is = (VideoState *) userdata;
VideoPicture *vp; vp = &is->pictq[is->pictq_windex];
if (vp->bmp) {
// we already have one make another, bigger/smaller
SDL_DestroyTexture(vp->bmp);
} if(vp->rawdata) {
av_free(vp->rawdata);
} // Allocate a place to put our YUV image on that screen
vp->screen = SDL_CreateWindow("My Player Window", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, is->video_st->codec->width,
is->video_st->codec->height,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL); vp->renderer = SDL_CreateRenderer(vp->screen, -1, 0);
vp->bmp = SDL_CreateTexture(vp->renderer, SDL_PIXELFORMAT_YV12,
SDL_TEXTUREACCESS_STREAMING, is->video_st->codec->width, is->video_st->codec->height); vp->width = is->video_st->codec->width;
vp->height = is->video_st->codec->height; AVFrame* pFrameYUV = avcodec_alloc_frame();
if (pFrameYUV == NULL)
return; int numBytes = avpicture_get_size(PIX_FMT_YUV420P, vp->width,
vp->height); uint8_t* buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t)); avpicture_fill((AVPicture *) pFrameYUV, buffer, PIX_FMT_YUV420P,
vp->width, vp->height); vp->rawdata = pFrameYUV; SDL_LockMutex(is->pictq_mutex);
vp->allocated = 1;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
} int queue_picture(VideoState *is, AVFrame *pFrame) {
VideoPicture *vp;
//int dst_pic_fmt
AVPicture pict; /* wait unitl we have space for a new pic */
SDL_LockMutex(is->pictq_mutex);
while (is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
SDL_UnlockMutex(is->pictq_mutex); if (is->quit)
return -1; // windex is set to 0 initially
vp = &is->pictq[is->pictq_windex]; /* allocate or resize the buffer ! */
if (!vp->bmp || vp->width != is->video_st->codec->width
|| vp->height != is->video_st->codec->height) {
SDL_Event event; vp->allocated = 0;
/* we have to do it in the main thread */
event.type = FF_ALLOC_EVENT;
event.user.data1 = is;
SDL_PushEvent(&event); /* wait until we have a picture allocated */
SDL_LockMutex(is->pictq_mutex);
while (!vp->allocated && !is->quit) {
SDL_CondWait(is->pictq_cond, is->pictq_mutex);
}
}
SDL_UnlockMutex(is->pictq_mutex);
if (is->quit) {
return -1;
} /* We have a place to put our picture on the queue */
if (vp->rawdata) {
// Convert the image into YUV format that SDL uses
sws_scale(is->sws_ctx, (uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, is->video_st->codec->height,
vp->rawdata->data, vp->rawdata->linesize); /* now we inform our display thread that we have a pic ready */
if (++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) {
is->pictq_windex = 0;
}
SDL_LockMutex(is->pictq_mutex);
is->pictq_size++;
SDL_UnlockMutex(is->pictq_mutex);
}
return 0;
} int video_thread(void *arg) {
VideoState *is = (VideoState *) arg;
AVPacket pkt1, *packet = &pkt1;
int frameFinished;
AVFrame *pFrame; pFrame = av_frame_alloc(); for (;;) {
if (packet_queue_get(&is->videoq, packet, 1) < 0) {
// means we quit getting packets
break;
} // Decode video frame
avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished,
packet); // Did we get a video frame?
if (frameFinished) {
if (queue_picture(is, pFrame) < 0) {
break;
}
}
av_free_packet(packet);
} av_free(pFrame);
return 0;
} int audio_stream_component_open(VideoState *is, int stream_index) {
AVFormatContext *ic = is->ic;
AVCodecContext *codecCtx;
AVCodec *codec;
SDL_AudioSpec wanted_spec, spec;
int64_t wanted_channel_layout = 0;
int wanted_nb_channels;
/* SDL支持的声道数为 1, 2, 4, 6 */
/* 后面我们会使用这个数组来纠正不支持的声道数目 */
const int next_nb_channels[] = { 0, 0, 1, 6, 2, 6, 4, 6 }; if (stream_index < 0 || stream_index >= ic->nb_streams) {
return -1;
} codecCtx = ic->streams[stream_index]->codec;
wanted_nb_channels = codecCtx->channels;
if (!wanted_channel_layout
|| wanted_nb_channels
!= av_get_channel_layout_nb_channels(
wanted_channel_layout)) {
wanted_channel_layout = av_get_default_channel_layout(
wanted_nb_channels);
wanted_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX;
} wanted_spec.channels = av_get_channel_layout_nb_channels(
wanted_channel_layout);
wanted_spec.freq = codecCtx->sample_rate;
if (wanted_spec.freq <= 0 || wanted_spec.channels <= 0) {
fprintf(stderr, "Invalid sample rate or channel count!\n");
return -1;
}
wanted_spec.format = AUDIO_S16SYS; // 具体含义请查看“SDL宏定义”部分
wanted_spec.silence = 0; // 0指示静音
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; // 自定义SDL缓冲区大小
wanted_spec.callback = audio_callback; // 音频解码的关键回调函数
wanted_spec.userdata = is; // 传给上面回调函数的外带数据 /* 打开音频设备,这里使用一个while来循环尝试打开不同的声道数(由上面 */
/* next_nb_channels数组指定)直到成功打开,或者全部失败 */
while (SDL_OpenAudio(&wanted_spec, &spec) < 0) {
fprintf(stderr, "SDL_OpenAudio (%d channels): %s\n",
wanted_spec.channels, SDL_GetError());
wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
if (!wanted_spec.channels) {
fprintf(stderr,
"No more channel combinations to tyu, audio open failed\n");
return -1;
}
wanted_channel_layout = av_get_default_channel_layout(
wanted_spec.channels);
} /* 检查实际使用的配置(保存在spec,由SDL_OpenAudio()填充) */
if (spec.format != AUDIO_S16SYS) {
fprintf(stderr, "SDL advised audio format %d is not supported!\n",
spec.format);
return -1;
} if (spec.channels != wanted_spec.channels) {
wanted_channel_layout = av_get_default_channel_layout(spec.channels);
if (!wanted_channel_layout) {
fprintf(stderr, "SDL advised channel count %d is not supported!\n",
spec.channels);
return -1;
}
} /* 把设置好的参数保存到大结构中 */
is->audio_src_fmt = is->audio_tgt_fmt = AV_SAMPLE_FMT_S16;
is->audio_src_freq = is->audio_tgt_freq = spec.freq;
is->audio_src_channel_layout = is->audio_tgt_channel_layout =
wanted_channel_layout;
is->audio_src_channels = is->audio_tgt_channels = spec.channels; codec = avcodec_find_decoder(codecCtx->codec_id);
if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) {
fprintf(stderr, "Unsupported codec!\n");
return -1;
}
ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
switch (codecCtx->codec_type) {
case AVMEDIA_TYPE_AUDIO:
is->audioStream = stream_index;
is->audio_st = ic->streams[stream_index];
is->audio_buf_size = 0;
is->audio_buf_index = 0;
memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
packet_queue_init(&is->audioq);
SDL_PauseAudio(0); // 开始播放静音
break;
default:
break;
} return 0;
} int video_stream_component_open(VideoState *is, int stream_index) {
AVFormatContext *pFormatCtx = is->ic;
AVCodecContext *codecCtx;
AVCodec *codec; if (stream_index < 0 || stream_index >= pFormatCtx->nb_streams) {
return -1;
} // Get a pointer to the codec context for the video stream
codecCtx = pFormatCtx->streams[stream_index]->codec; codec = avcodec_find_decoder(codecCtx->codec_id);
if (!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) {
fprintf(stderr, "Unsupported codec!\n");
return -1;
} switch (codecCtx->codec_type) {
case AVMEDIA_TYPE_VIDEO:
is->videoStream = stream_index;
is->video_st = pFormatCtx->streams[stream_index];
is->sws_ctx = sws_getContext(is->video_st->codec->width,
is->video_st->codec->height, is->video_st->codec->pix_fmt,
is->video_st->codec->width, is->video_st->codec->height,
AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
packet_queue_init(&is->videoq);
is->video_tid = SDL_CreateThread(video_thread, "video_thread", is);
break;
default:
break;
}
return 0;
} int decode_thread(void *arg) {
VideoState *is = (VideoState *) arg;
AVFormatContext *pFormatCtx = NULL;
AVPacket pkt1, *packet = &pkt1; int video_index = -1;
int audio_index = -1;
int i; is->videoStream = -1;
is->audioStream = -1; AVIOInterruptCB interupt_cb; global_video_state = is; // will interrup blocking functions if we quit!
interupt_cb.callback = decode_interrupt_cb;
interupt_cb.opaque = is; if (avio_open2(&is->io_ctx, is->filename, 0, &interupt_cb, NULL)) {
fprintf(stderr, "Cannot open I/O for %s\n", is->filename);
return -1;
} //Open video file
if (avformat_open_input(&pFormatCtx, is->filename, NULL, NULL) != 0) {
return -1; //Couldn't open file
} is->ic = pFormatCtx; //Retrieve stream infomation
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, is->filename, 0); //Find the first video stream
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->coder_type == AVMEDIA_TYPE_VIDEO
&& video_index < 0) {
video_index = i;
} if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO
&& audio_index < 0) {
audio_index = i;
}
} if (audio_index >= 0) {
/* 所有设置SDL音频流信息的步骤都在这个函数里完成 */
audio_stream_component_open(is, audio_index);
} if (video_index >= 0) {
video_stream_component_open(is, video_index);
} if (is->videoStream < 0 || is->audioStream <= 0) {
fprintf(stderr, "%s: could not open codec\n", is->filename);
goto fail;
} //main decode loop
/* 读包的主循环, av_read_frame不停的从文件中读取数据包*/
for (;;) {
if (is->quit) {
break;
} //seek stuff goes here
/* 这里audioq.size是指队列中的所有数据包带的音频数据的总量或者视频数据总量,并不是包的数量 */
if (is->audioq.size > MAX_AUDIO_SIZE || is->videoq.size > MAX_VIDEO_SIZE) {
SDL_Delay(10);
continue;
}
if (av_read_frame(is->ic, packet) < 0) {
if (is->ic->pb->error == 0) {
SDL_Delay(100); /* no error; wait for user input */
continue;
} else {
break;
}
}
// Is this a packet from the video stream?
if (packet->stream_index == is->videoStream) {
packet_queue_put(&is->videoq, packet);
} else if (packet->stream_index == is->audioStream) {
packet_queue_put(&is->audioq, packet);
} else {
av_free_packet(packet);
}
} /*all done - wait for it*/
while (!is->quit) {
SDL_Delay(100);
} fail: if (1) {
SDL_Event event;
event.type = FF_QUIT_EVENT;
event.user.data1 = is;
SDL_PushEvent(&event);
}
return 0;
} int main(int argc, char *argv[]) {
char *filename = argv[1];
SDL_Event event; VideoState *is;
is = av_malloc(sizeof(VideoState)); // Register all formats and codecs
av_register_all(); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
} av_strlcpy(is->filename, filename, sizeof(is->filename)); is->pictq_mutex = SDL_CreateMutex();
is->pictq_cond = SDL_CreateCond(); schedule_refresh(is, 40); is->parse_tid = SDL_CreateThread(decode_thread, "parse_thread", is);
if (!is->parse_tid) {
av_free(is);
return -1;
} for (;;) {
SDL_WaitEvent(&event);
switch (event.type) {
case FF_QUIT_EVENT:
case SDL_QUIT:
SDL_CondSignal(is->audioq.cond);
SDL_CondSignal(is->videoq.cond);
is->quit = 1;
SDL_Quit();
return 0;
break;
case FF_ALLOC_EVENT:
alloc_picture(event.user.data1);
break; case FF_REFRESH_EVENT:
video_refresh_timer(event.user.data1);
break;
}
} return 0;
}

[原]如何在Android用FFmpeg+SDL2.0解码图像线程的更多相关文章

  1. [原]如何在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. 利用nodejs模块缓存机制创建“全局变量”

    在<深入浅出nodejs>有这样一段(有部分增减): 1.nodejs引入模块分四个步骤 路径分析 文件定位 编译执行 加入内存 2.核心模块部分在node源代码的编译过程中就编译成了二级 ...

  2. CSS关于元素垂直居中的问题

    今天碰到了一个问题,给一个父容器和一个子元素,子元素不定高和不定宽,怎么让子元素居中在父容器中,比如下段代码 方法1: <div class="div1"> <d ...

  3. paip.语义分析--单字词形容词表180个

    paip.语义分析--单字词形容词表180个  INSERT INTO t (word)  SELECT DISTINCT word FROM `word_main` where tsisin is ...

  4. paip.提升性能----jvm参数调整.txt

    paip.提升性能----jvm参数调整.txt 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.n ...

  5. 微信小程序笔记(二)

    微信小程序环境搭建与开发工具介绍 2-1 开篇介绍及下载工具 1.开发工具下载地址:   http://t.cn/RVKH0HS 2.下载安装对应版本:win32,win64,mac; 2-2 小程序 ...

  6. 销傲销售过程GSP管理系统功能概述

    1      公司介绍 西安海思威软件有限公司于2009年2月注册成立,海思威软件公司隶属于海思威集团,位于交通十分便利的西安经济技术开发区.公司致力于中国本土式销售管理的研究与管理软件产品的开发,是 ...

  7. mysql中You can’t specify target table for update in FROM clause错误解决方法

    mysql中You can't specify target table for update in FROM clause错误的意思是说,不能先select出同一表中的某些值,再update这个表( ...

  8. centos7开启3306端口,liunx查看防火墙是否开启

    Can't connect to MySQL server on localhost (10061)这个就属于下面要说的情况 启动服务 systemctl start mariadb.service ...

  9. ubuntu bless 16字节每行

    打开Preferences配置 输入路径:/usr/share/bless/bless-16-bytes-per-row.layout 或者使用以下配置 cat /home/scue/.config/ ...

  10. drupal7安装中文错误

    安装中文错误 星期四, 07/04/2013 - 15:06 — tao_3000 更多0 An AJAX HTTP error occurred. HTTP Result Code: 200 Deb ...