ffmpeg 播放音视频,time_base解决音频同步问题,SDL渲染画面
Posted 乘风偷月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ffmpeg 播放音视频,time_base解决音频同步问题,SDL渲染画面相关的知识,希望对你有一定的参考价值。
概要
csdn上的资料,让我受益匪浅。我也想借此机会,把一些之前写过的项目分享出去,让这些存放在电脑上的资料可以分享给更多的朋友,发挥更大的价值。当然,项目中可能还存在一些bug,希望有疑问的朋友提出来,咋们一起解决。加油,撸起袖子加油干!
安霸行车记录仪是一款携带Gps信息的行车记录仪、本文基于C++语言开发一套MFC框架的应用工具,解析安霸行车记录仪录制的mp4文件,获取音视频数据进行播放,并解析到gps数据显示到百度地图上,通过回放可以直观看到当前车辆所处的位置,同时,可以快速的浏览车辆是否按指定的路线行驶。有助于事后分析车辆的实际行驶路线。
ffmpeg 调用流程:
分配空间及初始化
av_mallocz();
av_register_all();
avformat_alloc_context();
打开文件
avformat_open_input();
查找解码器
avcodec_find_decoder();
打开解码器,分别有视频解码器AVMEDIA_TYPE_VIDEO,音频解码器AVMEDIA_TYPE_AUDIO,字幕解码器AVMEDIA_TYPE_SUBTITLE
avcodec_open2();
另外还有可以通过以下接口获取字幕解码器
avcodec_decode_subtitle2();
读取一帧数据
av_frame_alloc();
av_init_packet();
av_read_frame();
跳转到指定位置
av_seek_frame();
释放空间
av_frame_free();
av_free();
关闭
avcodec_close();
avformat_close_input();
视频的渲染使用SDL
具体的代码如下,这里只是逻辑上的代码,UI界面如有需要请联系本人,电话号码18824182332(微信同号),谢谢!!
/*
========================================================================
File name: ztplayerDll.h
Module:
Author: 中唐工作室(zt)18824182332
Create Time: 2016/12/10 10:41:00
Modify By:
Modify Date:
========================================================================
*/
#ifndef __ZONTTANG_ZTPLAYERDLL_H__
#define __ZONTTANG_ZTPLAYERDLL_H__
#define ZONGTANG_H_DLL_EXPORTS
#ifdef ZONGTANG_H_DLL_EXPORTS
#define ZONGTANGDLL_API __declspec(dllexport)
#else
#define ZONGTANGDLL_API __declspec(dllimport)
#endif
#define __STDC_CONSTANT_MACROS
#include "const.h"
typedef void(*FrameCallBack)(const AVPacket* packet);
typedef void(*FrameEndCallBack)();
typedef void(*AnalysisGpsEndCallBack)();
ZONGTANGDLL_API void initSDK(VideoState** p);
ZONGTANGDLL_API int openFile(char filepath[], int64_t& duration);
ZONGTANGDLL_API int setFrameCallback(FrameCallBack _callback);
ZONGTANGDLL_API int setFrameEndCallback(FrameEndCallBack _callback);
ZONGTANGDLL_API int initCodec();
ZONGTANGDLL_API int setWindownHandle(HWND handle);
ZONGTANGDLL_API int play();
ZONGTANGDLL_API int seek(int64_t timestamp);
ZONGTANGDLL_API int pause(bool enable);
ZONGTANGDLL_API bool getState();
ZONGTANGDLL_API int desSDK();
ZONGTANGDLL_API int setVolumeEnable(bool enable);
ZONGTANGDLL_API int setVolume(double value);
ZONGTANGDLL_API int analysisGps();
ZONGTANGDLL_API int setAnalysisGpsEndCallback(AnalysisGpsEndCallBack _callback);
ZONGTANGDLL_API int saveFrame(char** filename, LPTSTR Dir);
#endif
/*
========================================================================
File name: ztplayerDll.cpp
Module:
Author: 中唐工作室(zt)18824182332
Create Time: 2016/12/10 10:41:00
Modify By:
Modify Date:
========================================================================
*/
#pragma once
#include "stdafx.h"
#include "tools.h"
#include "mapUtils.h"
bool mPlaying = false;
VideoState *global_video_state;
FrameCallBack frameCallBack;
FrameEndCallBack frameEndCallBack;
AnalysisGpsEndCallBack analysisGpsEndCallBack;
PictureHandle* global_picture_handle;
BOOL m_Release;
#pragma region 音频模块
int audio_decode_frame(VideoState *is, double *pts_ptr)
int len1, len2, decoded_data_size, n;
AVPacket *pkt = &is->audio_pkt;
int got_frame = 0;
int64_t dec_channel_layout;
int wanted_nb_samples, resampled_data_size;
double pts = 0;
while (1)
while (is->audio_pkt_size > 0)
if (!is->audio_frame)
if (!(is->audio_frame = av_frame_alloc()))
return AVERROR(ENOMEM);
else
av_frame_unref(is->audio_frame);
if (is->audio_st == NULL)
break;
if (m_Release)
break;
/**
* 当AVPacket中装得是音频时,有可能一个AVPacket中有多个AVFrame,
* 而某些解码器只会解出第一个AVFrame,这种情况我们必须循环解码出后续AVFrame
*/
len1 = avcodec_decode_audio4(is->audio_st->codec, is->audio_frame, &got_frame, pkt);
if (len1 < 0)
is->audio_pkt_size = 0;
printf("break\\n");
break;
is->audio_pkt_data += len1;
is->audio_pkt_size -= len1;
if (got_frame <= 0)
continue;
//执行到这里我们得到了一个AVFrame
decoded_data_size = av_samples_get_buffer_size(NULL,
is->audio_frame->channels, is->audio_frame->nb_samples,
(AVSampleFormat)is->audio_frame->format, 1);
//得到这个AvFrame的声音布局,比如立体声
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);
//这个AVFrame每个声道的采样数
wanted_nb_samples = is->audio_frame->nb_samples;
/**
* 接下来判断我们之前设置SDL时设置的声音格式(AV_SAMPLE_FMT_S16),声道布局,
* 采样频率,每个AVFrame的每个声道采样数与
* 得到的该AVFrame分别是否相同,如有任意不同,我们就需要swr_convert该AvFrame,
* 然后才能符合之前设置好的SDL的需要,才能播放
*/
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,
(AVSampleFormat)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判断失败,就会初始化好swr_ctx,就会如期进行转换
*/
if (is->swr_ctx)
// const uint8_t *in[] = is->audio_frame->data[0] ;
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)
fprintf(stdout, "swr_set_compensation \\n");
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;
/**
* 转换该AVFrame到设置好的SDL需要的样子,有些旧的代码示例最主要就是少了这一部分,
* 往往一些音频能播,一些不能播,这就是原因,比如有些源文件音频恰巧是AV_SAMPLE_FMT_S16的。
* swr_convert 返回的是转换后每个声道(channel)的采样数
*/
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;
//每声道采样数 x 声道数 x 每个采样字节数
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];
pts = is->audio_clock;
*pts_ptr = pts;
n = 2 * is->audio_st->codec->channels;
is->audio_clock += (double)resampled_data_size / (double)(n * is->audio_st->codec->sample_rate);
return resampled_data_size;
if (pkt->data)
av_free_packet(pkt);
if (is->quit)
return -1;
if (!mPlaying)
if (is->audio_buf != NULL)
is->audio_buf_size = 1024;
memset(is->audio_buf, 0, is->audio_buf_size);
SDL_Delay(10);
continue;
if (tools::packet_queue_get(&is->audioq, pkt, is->decodeState) < 0)
return -1;
if (is->audio_clock < 0)
connect;
is->audio_pkt_data = pkt->data;
is->audio_pkt_size = pkt->size;
if (pkt->pts != AV_NOPTS_VALUE)
is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
void audio_callback(void *userdata, Uint8 *stream, int len)
VideoState *is = (VideoState *)userdata;
int len1, audio_data_size = 0;
double pts;
double delay = 0;
while (len > 0)
if (global_video_state->quit)
break;
if (m_Release)
return;
if (is->audio_buf_index >= is->audio_buf_size)
if (is->audioq.size > 0)
audio_data_size = audio_decode_frame(is, &pts);
fprintf(stderr, "audio_decode_frame failed\\n", audio_data_size);
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;
else
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;
/*打开音频有关设置
*/
int stream_component_open(VideoState *is, unsigned int stream_index)
global_video_state = is;
AVFormatContext *ic = global_video_state->pFormatCtx;
AVCodecContext *codecCtx;
SDL_Audiospec wanted_spec, spec;
int64_t wanted_channel_layout = 0;
int wanted_nb_channels;
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;
wanted_spec.silence = 0;
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = is;
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);
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;
ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
switch (codecCtx->codec_type)
case AVMEDIA_TYPE_AUDIO:
SDL_PauseAudio(0);
break;
default:
break;
return 0;
#pragma endregion
uint64_t global_video_pkt_pts = AV_NOPTS_VALUE;
double synchronize_video(VideoState *is, AVFrame *src_frame, double pts)
double frame_delay;
if (pts != 0)
is->video_clock = pts;
else
pts = is->video_clock;
is->video_dts = src_frame->pkt_dts;
frame_delay = av_q2d(is->video_st->codec->time_base);
frame_delay += src_frame->repeat_pict * (frame_delay * 0.5);
is->video_clock += frame_delay;
return pts;
void alloc_picture(void *userdata)
VideoState *is = (VideoState *)userdata;
VideoPicture *vp;
vp = &is->pictq[is->pictq_windex];
vp->width = is->video_st->codec->width;
vp->height = is->video_st->codec->height;
SDL_LockMutex(is->pictq_mutex);
vp->allocated = 1;
SDL_CondSignal(is->pictq_cond);
SDL_UnlockMutex(is->pictq_mutex);
static void display_picture(AVPacket *packet, AVFrame* pFrame)
if (global_video_state->quit)
return;
if (global_picture_handle == NULL)
return;
AVCodecContext * pCodecCtx = global_video_state->video_st->codec;
AVFrame *pFrameYUV = global_picture_handle->pFrameYUV;
global_picture_handle->pFrame = pFrame;//截图使用
static struct SwsContext *img_convert_ctx;
if (img_convert_ctx == NULL)
img_convert_ctx = sws_getContext(global_video_state->video_st->codec->width, global_video_state->video_st->codec->height,
global_video_state->video_st->codec->pix_fmt,
global_video_state->video_st->codec->width, global_video_state->video_st->codec->height,
PIX_FMT_YUV420P,
SWS_BICUBIC, NULL, NULL, NULL);
if (img_convert_ctx == NULL)
fprintf(stderr, "Cannot initialize the conversion context/n");
exit(1);
if (pFrame == NULL || pFrameYUV == NULL)
return;
sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
pFrameYUV->data, pFrameYUV->linesize);
if (pFrame == NULL || pFrameYUV == NULL)
return;
SDL_UpdateYUVTexture(global_picture_handle->sdlTexture, &global_picture_handle->srcRect,
pFrameYUV->data[0], pFrameYUV->linesize[0],
pFrameYUV->data[1], pFrameYUV->linesize[1],
pFrameYUV->data[2], pFrameYUV->linesize[2]);
SDL_RenderClear(global_picture_handle->sdlRenderer);
SDL_RenderCopy(global_picture_handle->sdlRenderer, global_picture_handle->sdlTexture, &global_picture_handle->srcRect, &global_picture_handle->sdlRect);
SDL_RenderPresent(global_picture_handle->sdlRenderer);
//SDL End-----------------------
double firt_subtitle_last_pts = 0;
double firt_subtitle_delay = 0;
void display_subtitle(VideoState *is)
AVPacket pkt1, *packet = &pkt1;
int frameFinished = 0;
if (global_video_state->quit)
return;
double actual_delay, delay, sync_threshold, ref_clock, diff;
delay = is->frame_last_pts - firt_subtitle_last_pts;
bool drag = false;//防止拖动时间不同步问题
if (delay <= 0 || delay >= 1.0)
delay = is->frame_last_delay;
drag = true;
if ((is->frame_last_pts <firt_subtitle_last_pts + firt_subtitle_delay) && !drag)
return;
if (tools::packet_queue_get(&is->subtitleq, packet, 0) > 0)
int len1 = avcodec_decode_subtitle2(is->subtitle_st->codec, is->pSubtitle, &frameFinished, (AVPacket*)packet);
firt_subtitle_last_pts = is->frame_last_pts;
double delay = is->pSubtitle->end_display_time - is->pSubtitle->start_display_time;
firt_subtitle_delay = delay / 1000.0f;
if (frameFinished > 0)
frameCallBack(packet);
int queue_picture(VideoState *is, AVFrame *pFrame, double pts, AVPacket* pkt)
VideoPicture *vp;
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 (global_video_state->quit)
return -1;
vp = &is->pictq[is->pictq_windex];
vp->pkt = pkt;
vp->pFrame = pFrame;
display_picture(pkt, pFrame);
//回调播放进度
if (frameCallBack != NULL)
frameCallBack(pkt);
vp->pts = pts;
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 len1, frameFinished;
AVFrame *pFrame;
double pts;
#if Debug
pFrame = avcodec_alloc_frame();
#else
pFrame = av_frame_alloc();
#endif
for (;;)
if (global_video_state->quit)
break;
if (m_Release)
break;
if (!mPlaying)
SDL_Delay(10);
continue;
if (tools::packet_queue_get(&is->videoq, packet, is->decodeState) < 0)
break;
pts = 0;
global_video_pkt_pts = packet->pts;
len1 = avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet);
if (packet->dts == AV_NOPTS_VALUE
&& pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE)
pts = *(uint64_t *)pFrame->opaque;
else if (packet->dts != AV_NOPTS_VALUE)
pts = packet->dts;
else
pts = 0;
pts *= av_q2d(is->video_st->time_base);
if (frameFinished)
pts = synchronize_video(is, pFrame, pts);
if (queue_picture(is, pFrame, pts, packet) < 0)
break;
av_free_packet(packet);
av_free(pFrame);
return 0;
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;
SDL_TimerID _timeId = 0;
static void schedule_refresh(VideoState *is, int delay)
_timeId = SDL_AddTimer(delay, sdl_refresh_timer_cb, is);
static void video_refresh_timer(void *userdata)
VideoState *is = (VideoState *)userdata;
VideoPicture *vp;
double actual_delay, delay, sync_threshold, ref_clock, diff;
if (is->pFormatCtx == NULL)
return;
if (is->video_st == NULL)
return;
if (global_video_state->quit)
if (_timeId != 0)
SDL_RemoveTimer(_timeId);
return;
double allTime = is->pFormatCtx->duration* av_q2d(is->video_st->time_base);
int64_t pos = global_video_pkt_pts* av_q2d(is->video_st->codec->pkt_timebase);
int64_t timestamp = is->pFormatCtx->duration / AV_TIME_BASE;
bool end = pos >= timestamp;
if (end)
//解析完成回调
if (frameEndCallBack != NULL)
printf(" acount: %d vcount: %d atime:%f vtime: %f\\n",
global_video_state->audioq.nb_packets, global_video_state->videoq.nb_packets,
global_video_state->audio_clock, global_video_state->video_clock);
frameEndCallBack();
if (is->video_st)
if (is->pictq_size == 0)
schedule_refresh(is, 10);
else
vp = &is->pictq[is->pictq_rindex];
delay = vp->pts - is->frame_last_pts;
if (delay <= 0 || delay >= 1.0)
delay = is->frame_last_delay;
is->frame_last_delay = delay;
is->frame_last_pts = vp->pts;
ref_clock = tools::get_audio_clock(is);
diff = vp->pts - ref_clock;
sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD;
if (fabs(diff) < AV_NOSYNC_THRESHOLD)
if (diff <= -sync_threshold)
delay = 0;
else if (diff >= sync_threshold)
delay = 2 * delay;
is->frame_timer += delay;
actual_delay = is->frame_timer - (tools::av_gettime() / 1000000.0);
if (actual_delay < 0.010)
actual_delay = 0.010;
schedule_refresh(is, (int)(actual_delay * 1000 + 0.5));
display_subtitle(is);
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);
int getCodeByType(AVMediaType type, AVCodecContext**pCodecCtx, AVCodec**pCodec)
int index = -1;
for (int i = 0; i < global_video_state->pFormatCtx->nb_streams; i++)
if (global_video_state->pFormatCtx->streams[i]->codec->codec_type == type)
index = i;
break;
if (index == -1)
return -1;
*pCodecCtx = global_video_state->pFormatCtx->streams[index]->codec;
*pCodec = avcodec_find_decoder((*pCodecCtx)->codec_id);
if (pCodec == NULL)
printf("Codec not found.\\n");
return -1;
//打开解码器
if (avcodec_open2(*pCodecCtx, *pCodec, NULL) < 0)
printf("Could not open codec.\\n");
return -1;
if (type == AVMEDIA_TYPE_VIDEO)
global_video_state->video_st = global_video_state->pFormatCtx->streams[index];
global_video_state->videoStream = index;
tools::packet_queue_init(&global_video_state->videoq);
tools::packet_queue_flush(&global_video_state->videoq);
global_video_state->frame_timer = (double)tools::av_gettime() / 1000000.0;
global_video_state->frame_last_delay = 40e-3;
int width = global_video_state->video_st->codec->width;
int height = global_video_state->video_st->codec->height;
AVPixelFormat pix_fmt = global_video_state->video_st->codec->pix_fmt;
global_picture_handle = (PictureHandle *)av_malloc(sizeof(PictureHandle));
global_picture_handle->pFrame = av_frame_alloc();
global_picture_handle->pFrameYUV = av_frame_alloc();
uint8_t *out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, width, height));
avpicture_fill((AVPicture *)global_picture_handle->pFrameYUV, out_buffer, PIX_FMT_YUV420P, width, height);
if (type == AVMEDIA_TYPE_AUDIO)
global_video_state->audioStream = index;
global_video_state->audio_st = global_video_state->pFormatCtx->streams[index];
global_video_state->audio_buf_size = 0;
global_video_state->audio_buf_index = 0;
memset(&global_video_state->audio_pkt, 0, sizeof(global_video_state->audio_pkt));
tools::packet_queue_init(&global_video_state->audioq);
tools::packet_queue_flush(&global_video_state->audioq);
if (type == AVMEDIA_TYPE_SUBTITLE)
global_video_state->subtitle_st = global_video_state->pFormatCtx->streams[index];
global_video_state->subtitleStream = index;
global_video_state->pSubtitle = (AVSubtitle *)av_malloc(sizeof(AVSubtitle));
tools::packet_queue_init(&global_video_state->subtitleq);
tools::packet_queue_flush(&global_video_state->subtitleq);
return index;
static int decode_thread(void *arg)
VideoState *is = (VideoState *)arg;
int ret = 0;
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
while (true)
if (global_video_state->quit)
break;
if (is->audioq.size > MAX_AUDIOQ_SIZE ||
is->videoq.size > MAX_VIDEOQ_SIZE)
SDL_Delay(10);
continue;
if (!mPlaying)
SDL_Delay(10);
continue;
ret = av_read_frame(is->pFormatCtx, packet);
if (ret < 0)
if (ret == AVERROR_EOF || url_feof(is->pFormatCtx->pb))
printf(" acount: %d vcount: %d atime:%f vtime: %f\\n",
is->audioq.nb_packets, is->videoq.nb_packets,
is->audio_clock, is->video_clock);
is->decodeState = 0;
break;
if (is->pFormatCtx->pb && is->pFormatCtx->pb->error)
break;
continue;
if (packet->stream_index == is->videoStream)
tools::packet_queue_put(&is->videoq, (AVPacket*)packet);
if (packet->stream_index == is->audioStream)
tools::packet_queue_put(&is->audioq, (AVPacket*)packet);
if (packet->stream_index == is->subtitleStream)
tools::packet_queue_put(&is->subtitleq, (AVPacket*)packet);
printf("decode_thread finish");
while (!is->quit)
SDL_Delay(10);
return 0;
LatLng lastGps;
static int analysis_thread(void *arg)
SDL_Delay(500);
VideoState *is = (VideoState *)arg;
int ret = 0;
int frameFinished = 0;
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(packet);
lastGps = LatLng();
AVSubtitle *pSubtitle = (AVSubtitle *)av_malloc(sizeof(AVSubtitle));
while (true)
if (global_video_state->quit)
break;
if (m_Release)
break;
ret = av_read_frame(is->pFormatCtx, packet);
if (ret < 0)
if (ret == AVERROR_EOF || url_feof(is->pFormatCtx->pb))
printf(" acount: %d vcount: %d atime:%f vtime: %f\\n",
is->audioq.nb_packets, is->videoq.nb_packets,
is->audio_clock, is->video_clock);
break;
if (is->pFormatCtx->pb && is->pFormatCtx->pb->error)
break;
continue;
if (is->quit)
break;
if (packet->stream_index == is->subtitleStream)
int len1 = avcodec_decode_subtitle2(is->subtitle_st->codec, pSubtitle, &frameFinished, (AVPacket*)packet);
if (len1 < 0)
continue;
if (frameFinished>0)
AVSubtitleRect* rect = *(pSubtitle->rects);
char * title = rect->ass;
std::vector<std::string> list = tools::split(title, ",");
string Time = "";
if (list.size() > 1)
Time = list.at(1);
string Lat = "0";
if (list.size() > 7)
Lat = list.at(7);
string Log = "0";
if (list.size() > 9)
Log = list.at(9);
string Speed = "";
if (list.size() > 11)
Speed = list.at(11);
string Date = "";
if (list.size() > 12)
Date = list.at(12);
double log = atof(Log.c_str());
double lat = atof(Lat.c_str());
if (log == 0 || lat == 0)
continue;
if (lastGps.lat != 0 && lastGps.lng != 0)
double tmpdistance1 = mapUtils::getDisance(lat, log, lastGps.lat, lastGps.lng);
double speed1 = atof(Speed.c_str());
speed1 = speed1*1.852;
double time1 = 10.0 / 100;
double speed2 = (tmpdistance1 / time1)* 3.6;
//发生漂移
if (speed2 > speed1)
continue;
lastGps = LatLng();
lastGps.lat = lat;
lastGps.lng = log;
LatLng latlnt = mapUtils::gpsToBaidu(atof(Log.c_str()), atof(Lat.c_str()));
MapInfo mapInfo = MapInfo();
mapInfo.lat = latlnt.lat;
mapInfo.lng = latlnt.lng;
is->gpsList.push_back(mapInfo);
av_seek_frame(is->pFormatCtx, is->videoStream, 0, AVSEEK_FLAG_BACKWARD);
printf("analysis_thread finish");
//解析完成
if (analysisGpsEndCallBack != NULL)
analysisGpsEndCallBack();
return 0;
ZONGTANGDLL_API void initSDK(VideoState** p)
global_video_state = (VideoState *)av_mallocz(sizeof(VideoState));
//初始化参数
global_video_state->videoStream = -1;
global_video_state->audioStream = -1;
global_video_state->subtitleStream = -1;
/*global_video_state->quit = 0;*/
*p = global_video_state;
//注册库中所有可用的文件格式和编码器
av_register_all();
//avformat_network_init();
//分配解码上下文
global_video_state->pFormatCtx = avformat_alloc_context();
m_Release = FALSE;
ZONGTANGDLL_API int openFile(char filepath[], int64_t& duration)
//打开输入文件
if (avformat_open_input(&global_video_state->pFormatCtx, filepath, NULL, NULL) != 0)
printf("Couldn't open input stream.\\n");
return -1;
//取出流信息
if (avformat_find_stream_info(global_video_state->pFormatCtx, NULL) < 0)
printf("Couldn't find stream information.\\n");
return -1;
//列出输入文件的相关流信息
av_dump_format(global_video_state->pFormatCtx, 0, filepath, 0);
if (global_video_state->pFormatCtx->duration != AV_NOPTS_VALUE)
int hours, mins, secs, us;
duration = global_video_state->pFormatCtx->duration;
secs = duration / AV_TIME_BASE;
us = duration % AV_TIME_BASE;
mins = secs / 60;
secs %= 60;
hours = mins / 60;
mins %= 60;
printf("%02d:%02d:%02d.%02d\\n", hours, mins, secs, (100 * us) / AV_TIME_BASE);
return 0;
ZONGTANGDLL_API int setFrameCallback(FrameCallBack _callback)
frameCallBack = _callback;
return 0;
ZONGTANGDLL_API int setFrameEndCallback(FrameEndCallBack _callback)
frameEndCallBack = _callback;
return 0;
ZONGTANGDLL_API int initCodec()
AVCodecContext *pCodecCtx, *sCodecCtx, *aCodecCtx;
AVCodec *pCodec, *sCodec, *aCodec;
int videoindex, subtitleindex, audioindex;
videoindex = getCodeByType(AVMEDIA_TYPE_VIDEO, &pCodecCtx, &pCodec);
if (videoindex == -1)
printf("Couldn't getCodeByType.\\n");
return -1;
subtitleindex = getCodeByType(AVMEDIA_TYPE_SUBTITLE, &sCodecCtx, &sCodec);
audioindex = getCodeByType(AVMEDIA_TYPE_AUDIO, &aCodecCtx, &aCodec);
if (audioindex >= 0)
stream_component_open(global_video_state, audioindex);
return 0;
ZONGTANGDLL_API int setWindownHandle(HWND handle)
if (global_picture_handle == NULL)
return -1;
if (global_video_state->video_st == NULL)
return -1;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
printf("Could not initialize SDL - %s\\n", SDL_GetError());
return -1;
SDL_Window *screen;
screen = SDL_CreateWindowFrom((void *)(handle));
if (!screen)
printf("SDL: could not create window - exiting:%s\\n", SDL_GetError());
return -1;
int iWidth = 0;
int iHeight = 0;
SDL_GetWindowSize(screen, &iWidth, &iHeight);
global_picture_handle->sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
int width = global_video_state->video_st->codec->width;
int height = global_video_state->video_st->codec->height;
global_picture_handle->sdlTexture = SDL_CreateTexture(global_picture_handle->sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
global_picture_handle->sdlRect.x = 0;
global_picture_handle->sdlRect.y = 0;
global_picture_handle->sdlRect.w = iWidth;
global_picture_handle->sdlRect.h = iHeight;
global_picture_handle->srcRect.x = 0;
global_picture_handle->srcRect.y = 0;
global_picture_handle->srcRect.w = width;
global_picture_handle->srcRect.h = height;
return 0;
//刷新线程,也叫守护线程
static int refresh_thread(void *arg)
SDL_Event event;
for (;;)
if (global_video_state == NULL)
break;
if (global_video_state->quit)
break;
SDL_WaitEvent(&event);
switch (event.type)
case FF_QUIT_EVENT:
case SDL_QUIT:
global_video_state->quit = 1;
break;
case FF_ALLOC_EVENT:
//alloc_picture(event.user.data1);
break;
case FF_REFRESH_EVENT:
video_refresh_timer(event.user.data1);
break;
default:
break;
return 0;
ZONGTANGDLL_API int play()
mPlaying = true;
global_video_state->quit = 0;
global_video_state->pictq_mutex = SDL_CreateMutex();
global_video_state->pictq_cond = SDL_CreateCond();
schedule_refresh(global_video_state, 40);
global_video_state->decodeState = 1;//重置解码状态为1
global_video_state->parse_tid = SDL_CreateThread(decode_thread, "myThread", global_video_state);
if (!global_video_state->parse_tid)
global_video_state->quit = 1;
av_free(global_video_state);
return -1;
global_video_state->video_tid = SDL_CreateThread(video_thread, "videoThread", global_video_state);
if (!global_video_state->video_tid)
global_video_state->quit = 1;
av_free(global_video_state);
return -1;
SDL_Thread *refresh_tid;
refresh_tid = SDL_CreateThread(refresh_thread, "myRefreshThread", NULL);
if (!refresh_tid)
global_video_state->quit = 1;
av_free(global_video_state);
return -1;
return 0;
ZONGTANGDLL_API int seek(int64_t timestamp)
int ret = 0;
//ret = pause(true);
tools::packet_queue_flush(&global_video_state->audioq);
tools::packet_queue_flush(&global_video_state->videoq);
tools::packet_queue_flush(&global_video_state->subtitleq);
if (ret < 0)
return -1;
if (av_seek_frame(global_video_state->pFormatCtx, global_video_state->videoStream, timestamp, AVSEEK_FLAG_BACKWARD) < 0)
printf("Could not seek_frame.\\n");
return -1;
/*ret = pause(false);
if (ret < 0)
return -1;
*/
return 0;
//true 为暂停 ,false 为继续
ZONGTANGDLL_API int pause(bool enable)
mPlaying = !enable;
if (global_video_state == NULL)
return -1;
if (global_video_state->quit)
return -1;
SDL_AudioStatus status = SDL_GetAudioStatus();
if (enable)
PacketQueue q = global_video_state->audioq;
SDL_CondSignal(q.cond);
SDL_PauseAudio(1);
printf("pause SDL_PauseAudio\\n");
else
PacketQueue q = global_video_state->audioq;
/*SDL_PauseAudio(SDL_AUDIO_PLAYING);*/
SDL_CondSignal(q.cond);
SDL_PauseAudio(0);
printf("pause SDL_AUDIO_PLAYING\\n");
return 0;
ZONGTANGDLL_API bool getState()
return mPlaying;
ZONGTANGDLL_API int desSDK()
try
if (global_video_state == NULL)
return -1;
global_video_state->quit = 1;
m_Release = TRUE;
PacketQueue q = global_video_state->audioq;
SDL_CondSignal(q.cond);
SDL_CloseAudio();
SDL_Delay(500);
if (global_picture_handle != NULL)
av_frame_free(&global_picture_handle->pFrameYUV);
global_picture_handle->sdlRenderer = NULL;
global_picture_handle->pSubtitle = NULL;
if (global_video_state != NULL)
tools::packet_queue_flush(&global_video_state->audioq);
tools::packet_queue_flush(&global_video_state->videoq);
tools::packet_queue_flush(&global_video_state->subtitleq);
if (global_video_state->video_st != NULL)
avcodec_close(global_video_state->video_st->codec);
global_video_state->video_st = NULL;
if (global_video_state->audio_st != NULL)
avcodec_close(global_video_state->audio_st->codec);
global_video_state->audio_st = NULL;
if (global_video_state->subtitle_st != NULL)
avcodec_close(global_video_state->subtitle_st->codec);
global_video_state->subtitle_st = NULL;
avformat_close_input(&global_video_state->pFormatCtx);
catch (...)
printf("Exception : \\n");
return -1;
return 0;
ZONGTANGDLL_API int setVolumeEnable(bool enable)
return 0;
ZONGTANGDLL_API int setVolume(double value)
return 0;
ZONGTANGDLL_API int analysisGps()
if (global_video_state == NULL)
return -1;
global_video_state->gpsList.clear();
global_video_state->parse_tid = SDL_CreateThread(analysis_thread, "myAnalysisThread", global_video_state);
if (!global_video_state->parse_tid)
global_video_state->quit = 1;
av_free(global_video_state);
return -1;
SDL_Delay(100);
return 0;
ZONGTANGDLL_API int setAnalysisGpsEndCallback(AnalysisGpsEndCallBack _callback)
analysisGpsEndCallBack = _callback;
return 0;
int WriteJPEG(AVFrame* pFrame, int width, int height, int iIndex, char**filename, LPTSTR Dir)
// 输出文件路径
char out_file[MAX_PATH] = 0 ;
char acInputFileName[200] = '\\0' ;
WideCharToMultiByte(CP_OEMCP, NULL, Dir, -1, acInputFileName, 200, NULL, FALSE);
int val = sprintf_s(out_file, sizeof(out_file), "%s\\\\%d.jpg", acInputFileName, iIndex);
// 分配AVFormatContext对象
AVFormatContext* pFormatCtx = avformat_alloc_context();
// 设置输出文件格式
pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);
// 创建并初始化一个和该url相关的AVIOContext
if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0)
printf("Couldn't open output file.");
return -1;
// 构建一个新stream
AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);
if (pAVStream == NULL)
return -1;
// 设置该stream的信息
AVCodecContext* pCodecCtx = pAVStream->codec;
pCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = PIX_FMT_YUVJ420P;
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
av_dump_format(pFormatCtx, 0, out_file, 1);
// 查找解码器
AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec)
printf("Codec not found.");
return -1;
// 设置pCodecCtx的解码器为pCodec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
printf("Could not open codec.");
return -1;
//Write Header
avformat_write_header(pFormatCtx, NULL);
int y_size = pCodecCtx->width * pCodecCtx->height;
// 给AVPacket分配足够大的空间
AVPacket pkt;
av_new_packet(&pkt, y_size * 3);
//
int got_picture = 0;
int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);
if (ret < 0)
printf("Encode Error.\\n");
return -1;
if (got_picture == 1)
ret = av_write_frame(pFormatCtx, &pkt);
av_free_packet(&pkt);
//Write Trailer
av_write_trailer(pFormatCtx);
printf("Encode Successful.\\n");
if (pAVStream)
avcodec_close(pAVStream->codec);
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
char* res = new char[strlen(out_file) + 1];
strcpy_s(res, strlen(out_file) + 1, out_file);
*filename = res;
return 0;
ZONGTANGDLL_API int saveFrame(char** filename, LPTSTR Dir)
try
if (global_picture_handle == NULL)
return -1;
if (global_video_state == NULL)
return -1;
if (global_video_state->video_st == NULL)
return -1;
AVFrame *pFrame = global_picture_handle->pFrame;
AVCodecContext *pCodecCtx = global_video_state->video_st->codec;
int width = pCodecCtx->width;
int height = pCodecCtx->height;
int index = global_video_state->video_dts;
WriteJPEG(pFrame, width, height, index, filename, Dir);
catch (...)
printf("Exception : \\n");
return -1;
return 0;
CPP 依赖
Opencv 3.4 以上版本
项目地址:
暂无
后续全部代码将开放到git代码管理上,如您需要全部代码作为学习,也可以联系本人,电话号码18824182332(微信同号)。
音/视频点击播放报错
以上是关于ffmpeg 播放音视频,time_base解决音频同步问题,SDL渲染画面的主要内容,如果未能解决你的问题,请参考以下文章