音视频从入门到精通——FFmpeg之av

编程入门 行业动态 更新时间:2024-10-27 00:29:12

<a href=https://www.elefans.com/category/jswz/34/1768069.html style=音视频从入门到精通——FFmpeg之av"/>

音视频从入门到精通——FFmpeg之av

文章目录

  • 播放器进度条函数av_seek_frame分析
  • 代码

播放器进度条函数av_seek_frame分析

av_seek_frame函数分析

/*** Seek to the keyframe at timestamp.* 'timestamp' in 'stream_index'.** @param s media file handle* @param stream_index If stream_index is (-1), a default* stream is selected, and timestamp is automatically converted* from AV_TIME_BASE units to the stream specific time_base.* @param timestamp Timestamp in AVStream.time_base units*        or, if no stream is specified, in AV_TIME_BASE units.* @param flags flags which select direction and seeking mode* @return >= 0 on success*/
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,int flags);

参数说明:

s: AVFormatContext类型的多媒体文件句柄

stream_index : int类型表示要进行操作的流索引

timestamp: int64_t类型的时间戳,表示要跳转到的时间位置

flags : 跳转方法,主要有一下几种

#define AVSEEK_FLAG_BACKWARD 1 
///< seek backward seek到timestamp之前的最近关键帧#define AVSEEK_FLAG_BYTE 2 
///< seeking based on position in bytes 基于字节位置的跳转#define AVSEEK_FLAG_ANY 4 
///< seek to any frame, even non-keyframes 跳转到任意帧,不一定是关键帧#define AVSEEK_FLAG_FRAME 8 
///< seeking based on frame number 基于帧数量的跳转

flags 的值为1,2,4,8。表示为二进制也就是

0000 0001
0000 0010
0000 1000
1000 0000

所以,flags 应该是要通过或运算,加起来使用的。
使用方法

代码

#include <iostream>
#include <thread>
extern "C" {#include "libavformat/avformat.h"#include "libavcodec/avcodec.h"#include "libswscale/swscale.h"#include "libswresample/swresample.h"
}
using namespace std;#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"swscale.lib")
#pragma comment(lib,"swresample.lib")static double r2d(AVRational r)
{return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}void XSleep(int ms)
{//c++ 11chrono::milliseconds du(ms);this_thread::sleep_for(du);
}int main(int argc, char* argv[])
{cout << "Test Demux FFmpeg.club" << endl;const char* path = "D:\\javaCode\\androidmaniu2022\\FFmpeg\\input.mp4";//初始化封装库---declared deprecated//---av_register_all();//初始化网络库 (可以打开rtsp rtmp http 协议的流媒体视频)avformat_network_init();//注册解码器---declared deprecated//avcodec_register_all();//参数设置AVDictionary* opts = NULL;//设置rtsp流已tcp协议打开av_dict_set(&opts, "rtsp_transport", "tcp", 0);//网络延时时间av_dict_set(&opts, "max_delay", "500", 0);//解封装上下文AVFormatContext* ic = NULL;int re = avformat_open_input(&ic,path,0,  // 0表示自动选择解封器&opts //参数设置,比如rtsp的延时时间);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf) - 1);cout << "open " << path << " failed! :" << buf << endl;getchar();return -1;}cout << "open " << path << " success! " << endl;//获取流信息 re = avformat_find_stream_info(ic, 0);//总时长 毫秒int totalMs = ic->duration / (AV_TIME_BASE / 1000);cout << "totalMs = " << totalMs << endl;//打印视频流详细信息av_dump_format(ic, 0, path, 0);//音视频索引,读取时区分音视频int videoStream = 0;int audioStream = 1;//获取音视频流信息 (遍历,函数获取)for (int i = 0; i < ic->nb_streams; i++){AVStream* as = ic->streams[i];cout << "codec_id = " << as->codecpar->codec_id << endl;cout << "format = " << as->codecpar->format << endl;//音频 AVMEDIA_TYPE_AUDIOif (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){audioStream = i;cout << i << "音频信息" << endl;cout << "sample_rate = " << as->codecpar->sample_rate << endl;//AVSampleFormat;cout << "channels = " << as->codecpar->channels << endl;//一帧数据?? 单通道样本数 cout << "frame_size = " << as->codecpar->frame_size << endl;//1024 * 2 * 2 = 4096  fps = sample_rate/frame_size}//视频 AVMEDIA_TYPE_VIDEOelse if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){videoStream = i;cout << i << "视频信息" << endl;cout << "width=" << as->codecpar->width << endl;cout << "height=" << as->codecpar->height << endl;//帧率 fps 分数转换cout << "video fps = " << r2d(as->avg_frame_rate) << endl;}}//获取视频流videoStream = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);/////视频解码器打开///找到视频解码器AVCodec* vcodec = avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);if (!vcodec){cout << "can't find the codec id " << ic->streams[videoStream]->codecpar->codec_id;getchar();return -1;}cout << "find the AVCodec " << ic->streams[videoStream]->codecpar->codec_id << endl;AVCodecContext* vc = avcodec_alloc_context3(vcodec);///配置解码器上下文参数avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar);//八线程解码vc->thread_count = 8;///打开解码器上下文re = avcodec_open2(vc, 0, 0);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf) - 1);cout << "avcodec_open2  failed! :" << buf << endl;getchar();return -1;}cout << "video avcodec_open2 success!" << endl;/////音频解码器打开AVCodec* acodec = avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);if (!acodec){cout << "can't find the codec id " << ic->streams[audioStream]->codecpar->codec_id;getchar();return -1;}cout << "find the AVCodec " << ic->streams[audioStream]->codecpar->codec_id << endl;///创建解码器上下文呢AVCodecContext* ac = avcodec_alloc_context3(acodec);///配置解码器上下文参数avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar);//八线程解码ac->thread_count = 8;///打开解码器上下文re = avcodec_open2(ac, 0, 0);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf) - 1);cout << "avcodec_open2  failed! :" << buf << endl;getchar();return -1;}cout << "audio avcodec_open2 success!" << endl;///ic->streams[videoStream]//malloc AVPacket并初始化AVPacket* pkt = av_packet_alloc();AVFrame* frame = av_frame_alloc();//像素格式和尺寸转换上下文SwsContext* vctx = NULL;unsigned char* rgb = NULL;//音频重采样 上下文初始化SwrContext* actx = swr_alloc();actx = swr_alloc_set_opts(actx,av_get_default_channel_layout(2),	//输出格式AV_SAMPLE_FMT_S16,					//输出样本格式ac->sample_rate,					//输出采样率av_get_default_channel_layout(ac->channels),//输入格式ac->sample_fmt,ac->sample_rate,0, 0);re = swr_init(actx);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf) - 1);cout << "swr_init  failed! :" << buf << endl;getchar();return -1;}unsigned char* pcm = NULL;for (;;){int re = av_read_frame(ic, pkt);if (re != 0){//循环播放cout << "==============================end==============================" << endl;int ms = 3000; //三秒位置 根据时间基数(分数)转换long long pos = (double)ms / (double)1000 * r2d(ic->streams[pkt->stream_index]->time_base);av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);continue;}cout << "pkt->size = " << pkt->size << endl;//显示的时间cout << "pkt->pts = " << pkt->pts << "(单位:秒)" << endl;//转换为毫秒,方便做同步cout << "pkt->pts ms = " << pkt->pts * (r2d(ic->streams[pkt->stream_index]->time_base) * 1000) << "(单位:毫秒)" << endl;//解码时间cout << "pkt->dts = " << pkt->dts << endl;AVCodecContext* cc = 0;if (pkt->stream_index == videoStream){cout << "图像" << endl;cc = vc;}if (pkt->stream_index == audioStream){cout << "音频" << endl;cc = ac;}///解码视频//发送packet到解码线程  send传NULL后调用多次receive取出所有缓冲帧re = avcodec_send_packet(cc, pkt);//释放,引用计数-1 为0释放空间av_packet_unref(pkt);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf) - 1);cout << "avcodec_send_packet  failed! :" << buf << endl;continue;}for (;;){//从线程中获取解码接口,一次send可能对应多次receivere = avcodec_receive_frame(cc, frame);//直到收不到为止,break退出循环if (re != 0) break;if (cc == vc) {//视频cout << "recv frame format      = " << frame->format << endl;cout << "recv frame linesize[0] = " << frame->linesize[0] << endl;cout << "recv frame linesize[1] = " << frame->linesize[1] << endl;cout << "recv frame linesize[2] = " << frame->linesize[2] << endl;//cout << endl;}else {//音频cout << "recv frame format      = " << frame->format << endl;cout << "recv frame linesize[0] = " << frame->linesize[0] << endl;cout << "recv frame nb_samples  = " << frame->nb_samples << endl;}//视频if (cc == vc){vctx = sws_getCachedContext(vctx,	//传NULL会新创建frame->width, frame->height,	//输入的宽高(AVPixelFormat)frame->format,	//输入格式 YUV420pframe->width, frame->height,	//输出的宽高AV_PIX_FMT_RGBA,				//输出格式RGBASWS_BILINEAR,					//尺寸变化的算法0, 0, 0);//if(vctx)//cout << "像素格式尺寸转换上下文创建或者获取成功!" << endl;//else//	cout << "像素格式尺寸转换上下文创建或者获取失败!" << endl;if (vctx){if (!rgb) rgb = new unsigned char[frame->width * frame->height * 4];uint8_t* data[2] = { 0 };data[0] = rgb;int lines[2] = { 0 };lines[0] = frame->width * 4;re = sws_scale(vctx,frame->data,		//输入数据frame->linesize,	//输入行大小0,frame->height,		//输入高度data,				//输出数据和大小lines);cout << "sws_scale = " << re << endl;}}else //音频{uint8_t* data[2] = { 0 };if (!pcm) pcm = new uint8_t[frame->nb_samples * 2 * 2];data[0] = pcm;re = swr_convert(actx,data, frame->nb_samples,		//输出(const uint8_t**)frame->data, frame->nb_samples	//输入);cout << "swr_convert = " << re << endl;}}//XSleep(500);}av_frame_free(&frame);av_packet_free(&pkt);if (ic){//释放封装上下文,并且把ic置0avformat_close_input(&ic);}getchar();return 0;
}

参考
ffmpeg中av_seek_frame使用样例
FFMPEG av_seek_frame

更多推荐

音视频从入门到精通——FFmpeg之av

本文发布于:2024-03-10 01:46:45,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1726693.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:音视频   入门   av   FFmpeg

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!