#include <unistd.h>
#include "define.h" //afxstd MFC
#include <iostream>
# include <stdio.h>
# include <assert.h>
# include <SDL2/SDL.h>
# include <SDL2/SDL_thread.h>
# include <SDL2/SDL_mutex.h>
extern "C"
{
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libswscale/swscale.h>
# include <libswresample/swresample.h>
}
using namespace std;
AVFormatContext* pFormat = NULL;
const char* path = "11.mp4";
//const char* path = "https://cctv5alih5c.v.myalicdn.com/live/cdrmcctv5_1td.m3u8";
//const char* path = "rtsp:/192.168.1.7:8554/";
AVDictionary* opt = NULL;
AVPacket* packet = NULL;
SwsContext * swsCtx = NULL;
AVFrame* frame = NULL;
AVFrame* frameRGB = NULL;
//寻找流
int VideoStream = -1;
int AudioStream = -1;
//读帧
int go = 0;
int FrameCount = 0;
int ret = 0;
int width = 0;
int height = 0;
int fmt = 0;
const int win_width = 720;
const int win_height = 480;
GLuint _texture;
void Initlize();
void Render();
void ShutDown();
void Initalize_ffmpeg();
unsigned CreateTextrue(int w,int h ,void* data);
typedef struct PacketQueue
{
AVPacketList *first_pkt; // 队头
AVPacketList *last_pkt; // 队尾
int nb_packets; //包的个数
int size; // 占用空间的字节数
SDL_mutex* mutext; // 互斥信号量
SDL_cond* cond; // 条件变量
}PacketQueue;
PacketQueue audioq;
int quit = 0;
AVFrame wanted_frame;
// 包队列初始化
void packet_queue_init(PacketQueue* q)
{
//memset(q, 0, sizeof(PacketQueue));
q->last_pkt = nullptr;
q->first_pkt = nullptr;
q->mutext = SDL_CreateMutex();
q->cond = SDL_CreateCond();
}
// 放入packet到队列中,不带头指针的队列
int packet_queue_put(PacketQueue*q, AVPacket *pkt)
{
AVPacketList *pktl;
if (av_dup_packet(pkt) < 0)
return -1;
pktl = (AVPacketList*)av_malloc(sizeof(AVPacketList));
if (!pktl)
return -1;
pktl->pkt = *pkt;
pktl->next = nullptr;
SDL_LockMutex(q->mutext);
if (!q->last_pkt) // 队列为空,新插入元素为第一个元素
q->first_pkt = pktl;
else // 插入队尾
q->last_pkt->next = pktl;
q->last_pkt = pktl;
q->nb_packets++;
q->size += pkt->size;
SDL_CondSignal(q->cond);
SDL_UnlockMutex(q->mutext);
return 0;
}
// 从队列中取出packet
static int packet_queue_get(PacketQueue* q, AVPacket* pkt, bool block)
{
AVPacketList* pktl;
int ret;
SDL_LockMutex(q->mutext);
while (true)
{
if (quit)
{
ret = -1;
break;
}
pktl = q->first_pkt;
if (pktl)
{
q->first_pkt = pktl->next;
if (!q->first_pkt)
q->last_pkt = nullptr;
q->nb_packets--;
q->size -= pktl->pkt.size;
*pkt = pktl->pkt;
av_free(pktl);
ret = 1;
break;
}
else if (!block)
{
ret = 0;
break;
}
else
{
SDL_CondWait(q->cond, q->mutext);
}
}
SDL_UnlockMutex(q->mutext);
return ret;
}
// 解码音频数据
int audio_decode_frame(AVCodecContext* aCodecCtx, uint8_t* audio_buf, int buf_size)
{
static AVPacket pkt;
static uint8_t* audio_pkt_data = nullptr;
static int audio_pkt_size = 0;
static AVFrame frame;
int len1;
int data_size = 0;
SwrContext* swr_ctx = nullptr;
while (true)
{
while (audio_pkt_size > 0)
{
int got_frame = 0;
len1 = avcodec_decode_audio4(aCodecCtx, &frame, &got_frame, &pkt);
if (len1 < 0) // 出错,跳过
{
audio_pkt_size = 0;
break;
}
audio_pkt_data += len1;
audio_pkt_size -= len1;
data_size = 0;
if (got_frame)
{
data_size = av_samples_get_buffer_size(nullptr, aCodecCtx->channels, frame.nb_samples, aCodecCtx->sample_fmt, 1);
assert(data_size <= buf_size);
memcpy(audio_buf, frame.data[0], data_size);
}
if (frame.channels > 0 && frame.channel_layout == 0)
frame.channel_layout = av_get_default_channel_layout(frame.channels);
else if (frame.channels == 0 && frame.channel_layout > 0)
frame.channels = av_get_channel_layout_nb_channels(frame.channel_layout);
if (swr_ctx)
{
swr_free(&swr_ctx);
swr_ctx = nullptr;
}
swr_ctx = swr_alloc_set_opts(nullptr, wanted_frame.channel_layout, (AVSampleFormat)wanted_frame.format, wanted_frame.sample_rate,
frame.channel_layout, (AVSampleFormat)frame.format, frame.sample_rate, 0, nullptr);
if (!swr_ctx || swr_init(swr_ctx) < 0)
{
cout << "swr_init failed:" << endl;
break;
}
int dst_nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx, frame.sample_rate) + frame.nb_samples,
wanted_frame.sample_rate, wanted_frame.format,AVRounding(1));
int len2 = swr_convert(swr_ctx, &audio_buf, dst_nb_samples,
(const uint8_t**)frame.data, frame.nb_samples);
if (len2 < 0)
{
cout << "swr_convert failed\n";
break;
}
return wanted_frame.channels * len2 * av_get_bytes_per_sample((AVSampleFormat)wanted_frame.format);
if (data_size <= 0)
continue; // No data yet,get more frames
return data_size; // we have data,return it and come back for more later
}
if (pkt.data)
av_free_packet(&pkt);
if (quit)
return -1;
if (packet_queue_get(&audioq, &pkt, true) < 0)
return -1;
audio_pkt_data = pkt.data;
audio_pkt_size = pkt.size;
}
}
static const int MAX_AUDIO_FRAME_SIZE = 192000;
static const int SDL_AUDIO_BUFFER_SIZE = 1024;
// 解码后的回调函数
void audio_callback(void* userdata, Uint8* stream, int len)
{
AVCodecContext* aCodecCtx = (AVCodecContext*)userdata;
int len1, audio_size;
static uint8_t audio_buff[(MAX_AUDIO_FRAME_SIZE * 3) / 2];
static unsigned int audio_buf_size = 0;
static unsigned int audio_buf_index = 0;
SDL_memset(stream, 0, len);
while (len > 0)
{
if (audio_buf_index >= audio_buf_size)
{
audio_size = audio_decode_frame(aCodecCtx, audio_buff, sizeof(audio_buff));
if (audio_size < 0)
{
audio_buf_size = 1024;
memset(audio_buff, 0, audio_buf_size);
}
else
audio_buf_size = audio_size;
audio_buf_index = 0;
}
len1 = audio_buf_size - audio_buf_index;
if (len1 > len)
len1 = len;
SDL_MixAudio(stream, audio_buff + audio_buf_index, len, SDL_MIX_MAXVOLUME);
//memcpy(stream, (uint8_t*)(audio_buff + audio_buf_index), audio_buf_size);
len -= len1;
stream += len1;
audio_buf_index += len1;
}
}
int main(int argc,char* argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(100, 100);
glutInitWindowSize(WIN_X,WIN_Y);
glutCreateWindow("XOpenGL");
GLenum error = glewInit();//扩展函数可以被使用了
if (GLEW_OK!= error)
{
printf("glewInit error\n");
glewGetErrorString(error);
return -1;
}
Initalize_ffmpeg();
Initlize();
//渲染
glutDisplayFunc(Render);
//刷新
glutIdleFunc(Render);
glutMainLoop();//消息队列
ShutDown();
return 0;
}
void Initlize()
{
//清除原色 刷成红色
glClearColor(1,0,0,1);
//视口
glViewport(0,0,WIN_X,WIN_Y);
//2D
glOrtho(0, WIN_X, WIN_Y,0,-100,100);
_texture = CreateTextrue(width, height, 0);
}
void ShutDown()
{
sws_freeContext(swsCtx);
av_frame_free(&frame);
av_frame_free(&frameRGB);
avformat_close_input(&pFormat);
}
void Render()
{
while (av_read_frame(pFormat, packet) >= 0)
{
//判断stream_index
if ((packet)->stream_index == AVMEDIA_TYPE_VIDEO)
{
//vCodec pFormat->streams[VideoStream]->codec
ret = avcodec_decode_video2(pFormat->streams[VideoStream]->codec, frame, &go, packet);
if (ret<0)
{
printf(" avcodec_decode_video2 failed\n");
return;
}
if (go)
{
sws_scale(swsCtx,
(const uint8_t**)frame->data,
frame->linesize,
0,
height,
frameRGB->data,
frameRGB->linesize
);
清除颜色缓冲区
glClear(GL_COLOR_BUFFER_BIT);
glm::vec2 Vertex[] =
{
glm::vec2{ 0.0f ,0.0f },
glm::vec2{ 0.0f, height },
glm::vec2{ width ,height},
glm::vec2{ width,0.0f },
};
glm::vec2 texture[] =
{
glm::vec2{ 0.0f,0.0f },
glm::vec2{ 0.0f,1.0f },
glm::vec2{ 1.0f,1.0f },
glm::vec2{ 1.0f,0.0f },
};
//GL_LINES GL_QUADS GL_TRIANGLES
glBindTexture(GL_TEXTURE_2D, _texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, frameRGB->data[0]);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
//1、sizeof(Vertex)/sizeof(Vertex[0])
//2、_countof _countof(Vertex)
for (size_t i = 0; i < sizeof(Vertex) / sizeof(Vertex[0]); i++)
{
glTexCoord2fv(glm::value_ptr(texture[i]));
glVertex2fv(glm::value_ptr(Vertex[i]));
}
glEnd();
//渲染
glutSwapBuffers();
usleep(4000);//多线程操作
FrameCount++;
printf("frame index:%d \n", FrameCount++);
}
}
if ((packet)->stream_index == AVMEDIA_TYPE_AUDIO)
{
packet_queue_put(&audioq, packet);
}
}
}
void Initalize_ffmpeg()
{
//测试DLL
printf("%s\n", avcodec_configuration());
//注册DLL
av_register_all();
//网络
avformat_network_init();
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8
//const char* path = "http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8";
//设置网络信息
const char* path = "11.mp4";
av_dict_set(&opt, "rtsp_transport", "udp", 0);
av_dict_set(&opt, "max_delay", "1000", 0);
ret = avformat_open_input(&pFormat, path, NULL, &opt);
if (ret)
{
printf(" avformat_open_input failed\n");
return;
}
printf(" avformat_open_input success\n");
//寻找流信息 =》 H264 width height
ret = avformat_find_stream_info(pFormat, NULL);
if (ret)
{
printf(" avformat_find_stream_info failed\n");
return;
}
printf(" avformat_find_stream_info success\n");
int time = pFormat->duration;
int mbittime = (time / 1000000) / 60;
int mmintime = (time / 1000000) % 60;
printf("%d分:%d秒\n", mbittime, mmintime);
av_dump_format(pFormat, NULL, path, 0);
VideoStream = av_find_best_stream(pFormat, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL); // 感觉返回值是个 序号
AudioStream = av_find_best_stream(pFormat, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, NULL);
AVCodecContext* pCodecCtxOrg = nullptr;
pCodecCtxOrg = pFormat->streams[AudioStream]->codec; // codec context
//AudioStream = av_find_best_stream(pFormat, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL);
AVCodec* vCodec = avcodec_find_decoder(pFormat->streams[VideoStream]->codec->codec_id);
if (!vCodec)
{
printf(" avcodec_find_decoder failed\n");
return;
}
AVCodec* aCodec = avcodec_find_decoder(pFormat->streams[AudioStream]->codec->codec_id);
if (!aCodec)
{
printf(" avcodec_find_decoder failed\n");
return;
}
printf(" avcodec_find_decoder success\n");
ret = avcodec_open2(pFormat->streams[VideoStream]->codec,
vCodec, NULL);
if (ret)
{
printf(" avcodec_open2 failed\n");
return;
}
printf(" avcodec_open2 success\n");
AVCodecContext* pCodecCtx = nullptr;
// 不直接使用从AVFormatContext得到的CodecContext,要复制一个
pCodecCtx = avcodec_alloc_context3(aCodec);
if (avcodec_copy_context(pCodecCtx, pCodecCtxOrg) != 0)
{
cout << "Could not copy codec context!" << endl;
return;
}
// Set audio settings from codec info
SDL_AudioSpec wanted_spec, spec;
wanted_spec.freq = pCodecCtx->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = pCodecCtx->channels;
wanted_spec.silence = 0;
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = pCodecCtx;
if (SDL_OpenAudio(&wanted_spec, &spec) < 0)
{
cout << "Open audio failed:" << SDL_GetError() << endl;
getchar();
return;
}
wanted_frame.format = AV_SAMPLE_FMT_S16;
wanted_frame.sample_rate = spec.freq;
wanted_frame.channel_layout = av_get_default_channel_layout(spec.channels);
wanted_frame.channels = spec.channels;
avcodec_open2(pCodecCtx, aCodec, nullptr);
packet_queue_init(&audioq);
SDL_PauseAudio(0);
//开始解码视频
//申请原始空间 =》创建帧空间
frame = av_frame_alloc();
frameRGB = av_frame_alloc();
width = pFormat->streams[VideoStream]->codec->width;
height = pFormat->streams[VideoStream]->codec->height;
fmt = pFormat->streams[VideoStream]->codec->pix_fmt;
//分配空间 进行图像转换
int nSize = avpicture_get_size(AV_PIX_FMT_RGB24,
width, height);
uint8_t* buff = NULL;
buff = (uint8_t*)av_malloc(nSize);
//一帧图像
avpicture_fill((AVPicture*)frameRGB, buff, AV_PIX_FMT_RGB24, width, height);
//av_malloc 等价于 malloc()
packet = (AVPacket*)av_malloc(sizeof(AVPacket));
//转换上下文
//swsCtx = sws_getCachedContext(swsCtx,XXXX)
swsCtx = sws_getContext(width, height, (AVPixelFormat)fmt,
width, height, AV_PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
}
unsigned CreateTextrue(int w, int h, void * data)
{
unsigned textid;
glGenTextures(1, &textid);
glBindTexture(GL_TEXTURE_2D, textid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
return textid;
}