/*
* auth : 杜竞宁
*
* time : 2021.12
*
* ffmpeg version : 4.4
* SDL version : 2.0.18
*
* */
#include <SDL.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include <ctime>
#include <mutex>
#include<queue>
#include <Log.h>
/* 跨平台且易用 */
#define sleep(for_seconds) std::this_thread::sleep_for(std::chrono::seconds(for_seconds))
#define usleep(for_milliseconds) std::this_thread::sleep_for(std::chrono::milliseconds(for_milliseconds))
extern "C"{
#include <stdlib.h>
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
std::string logFile = "log.txt";
Dfile Df(logFile);
#define log(data) Df.write(data)
void SaveFrame2PPM(AVFrame *pFrame, int width, int height, int iFrame) {
std::ofstream mediaFile;
std::string fileName = std::string("frame") + std::to_string(iFrame) + std::string(".ppm");
mediaFile.open(fileName.c_str(), std::ios::out);
if (mediaFile.is_open())
{
std::string head = std::string("P6\n") + std::to_string(width) + std::string(" ") + std::to_string(height) + std::string("\n255\n");
mediaFile.write(head.c_str(), head.length());
for(int i = 0; i < height; i++)
{
mediaFile.write((const char*)(pFrame->data[0] + i*pFrame->linesize[0]), width*3);
}
}
mediaFile.close();
}
typedef struct node{
Uint8* audio_point;
Uint32 len;
}Node;
std::queue<Node> AudioQUeue;
std::mutex AudioQueueMutex;
void inputQueue(Node nd)
{
AudioQueueMutex.lock();
if (nd.len > 0)
{
AudioQUeue.push(nd);
}
AudioQueueMutex.unlock();
}
Node outputQueue()
{
Node nd;
nd.audio_point = NULL;
nd.len = 0;
AudioQueueMutex.lock();
if (AudioQUeue.size() <= 0)
{
return nd;
}
nd = AudioQUeue.back();
AudioQueueMutex.unlock();
return nd;
}
void get_audio_date_cb(void *userdata, Uint8 *stream, int len){
static uint8_t* audio_chunk = NULL;
static Uint32 audio_len = 0;
static uint8_t* audio_pos = NULL;
std::cout << "get_audio_date_cb_________________________________=================" << std::endl;
if (audio_len <= 0)
{
if (audio_chunk){
av_freep(audio_chunk);
}
Node nd = outputQueue();
if ( nd.len <= 0 ) {
return;
} else {
audio_len = nd.len;
audio_pos = nd.audio_point;
audio_chunk = nd.audio_point;
}
}
SDL_memset(stream, 0, len);
if (audio_len <= 0){
return;
}
SDL_MixAudio(stream, audio_pos, (int)audio_len, SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
int main(int argc, char* argv[]){
if (argc <= 1)
{
std::cout << "usage: ./execute [fileName]" << std::endl;
return -1;
}
AVFormatContext *pFmtCtx = NULL;
/* 打开输入流,自动检测格式 */
AVDictionary *opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
if ( avformat_open_input(&pFmtCtx, argv[1], NULL, &opts) != 0 )
{
std::cout << argv[1] << " open File error" << std::endl;
avformat_close_input(&pFmtCtx);
return -1;
}
if ( avformat_find_stream_info(pFmtCtx, NULL) < 0 )
{
std::cout << argv[1] << " avformat_find_stream_info error" << std::endl;
avformat_close_input(&pFmtCtx);
return -1;
}
/* 注意:参数4 - 0是打印输入流 1是打印输出流 */
av_dump_format(pFmtCtx, 0, argv[1], 0);
int vStreamIndex = -1, aStreamIndex = -1;
for (unsigned int i = 0; i < pFmtCtx->nb_streams; i++ )
{
int codec_type = -999, codec_id = -999;
if ( pFmtCtx->streams[i] && pFmtCtx->streams[i]->codecpar )
{
codec_type = pFmtCtx->streams[i]->codecpar->codec_type;
codec_id = pFmtCtx->streams[i]->codecpar->codec_id;
}
if (codec_type == AVMEDIA_TYPE_VIDEO) // && codec_id == AV_CODEC_ID_H264
{
std::cout << "got video stream " << i << std::endl;
vStreamIndex = i;
} else if ( codec_type == AVMEDIA_TYPE_AUDIO && (codec_id == AV_CODEC_ID_AAC || codec_id == AV_CODEC_ID_AAC_LATM) ){
std::cout << "got audio stream " << i << std::endl;
aStreamIndex = i;
}
}
/* 处理数据 */
int i = 0;
AVPacket packet;
AVFrame *pFrame = NULL;
AVFrame *frameRGB = NULL;
AVCodec *pCodec = NULL;
AVCodec *pAudioCodec = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodecContext *pAudioCodecCtx = NULL;
if ( vStreamIndex >= 0 )
{
/* video codec */
AVCodecParameters *codecpar = pFmtCtx->streams[vStreamIndex]->codecpar;
pCodec = avcodec_find_decoder(codecpar->codec_id);
pCodecCtx = avcodec_alloc_context3(pCodec);
if ( !pCodecCtx || avcodec_parameters_to_context(pCodecCtx, codecpar) < 0 )
{
std::cout << argv[1] << " avcodec_parameters_to_context error" << std::endl;
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
if ( avcodec_open2(pCodecCtx, pCodec, NULL) < 0 )
{
std::cout << argv[1] << " avcodec_open2 error" << std::endl;
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
}
if ( aStreamIndex >= 0 )
{
/* audio codec */
AVCodecParameters *codecpar = pFmtCtx->streams[aStreamIndex]->codecpar;
pAudioCodec = avcodec_find_decoder(codecpar->codec_id);
pAudioCodecCtx = avcodec_alloc_context3(pAudioCodec);
if ( !pAudioCodecCtx || avcodec_parameters_to_context(pAudioCodecCtx, codecpar) < 0 )
{
std::cout << argv[1] << " avcodec_parameters_to_context error" << std::endl;
avcodec_free_context(&pAudioCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
if ( avcodec_open2(pAudioCodecCtx, pAudioCodec, NULL) < 0 )
{
std::cout << argv[1] << " avcodec_open2 error" << std::endl;
avcodec_free_context(&pAudioCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
}
/* audio convert */
int audio_channls = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
int audio_buffer_size = av_samples_get_buffer_size(NULL, audio_channls, 1024, AV_SAMPLE_FMT_S16, 1);
// uint8_t *avio_buffer = (uint8_t *)av_malloc(audio_buffer_size * 2);
log( "audio_buffer_size:" + std::to_string(audio_buffer_size));
/* frame where to save */
frameRGB = av_frame_alloc();
if ( !frameRGB )
{
std::cout << argv[1] << " frameRGB = av_frame_alloc(); error" << std::endl;
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
std::cout << "got audio stream ======================" << std::endl;
/* 填充方式 1 */
if(pCodecCtx)
{
frameRGB->format = AV_PIX_FMT_RGB24;
frameRGB->width = pCodecCtx->width;
frameRGB->height = pCodecCtx->height;
if ( av_frame_get_buffer(frameRGB, 1) < 0 )
{
std::cout << argv[1] << " av_frame_get_buffer error" << pCodecCtx->width <<std::endl;
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
}
/* 填充方式 2
uint8_t *buffer = NULL;
int numBytes = -1;
numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
if (numBytes < 0)
{
std::cout << argv[1] << " av_image_get_buffer_size error" << std::endl;
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
if ( !buffer )
{
std::cout << argv[1] << " buffer = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t)); error" << std::endl;
av_frame_free(&frameRGB);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
if ( av_image_fill_arrays(frameRGB->data, frameRGB->linesize,buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1) < 0 )
{
std::cout << argv[1] << " av_image_fill_arrays error" << std::endl;
av_freep(&buffer);
av_frame_free(&frameRGB);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
*/
SwsContext *swsCtx = NULL;
swsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, NULL, NULL, NULL );
if ( !swsCtx )
{
std::cout << argv[1] << " frameRGB = av_frame_alloc(); error" << std::endl;
av_frame_free(&frameRGB);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
pFrame = av_frame_alloc();
if ( !pFrame )
{
std::cout << argv[1] << " frameRGB = av_frame_alloc(); error" << std::endl;
sws_freeContext(swsCtx);
av_frame_free(&frameRGB);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
/* ******************* SDL init Video ***************************** */
/* 窗口 */
SDL_Window* Window = NULL;
/* 渲染 */
SDL_Renderer* Renderer = NULL;
SDL_Surface* PrimarySurface = NULL;
/* 纹理 */
SDL_Texture* Texture = NULL;
SDL_Rect Rect;
const int WindowWidth = pCodecCtx->width;
const int WindowHeight = pCodecCtx->height;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0)
{
log("SDL_Init Error" + std::string(SDL_GetError()));
return -1;
}
if(!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
log("SDL_SetHint Error" + std::string(SDL_GetError()));
}
if((Window = SDL_CreateWindow("SDLPlayer", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WindowWidth, WindowHeight, SDL_WINDOW_SHOWN) ) == NULL)
{
log("SDL_CreateWindow Error" + std::string(SDL_GetError()));
return -1;
}
if ((PrimarySurface = SDL_GetWindowSurface(Window)) == NULL )
{
log("SDL_GetWindowSurface Error");
return -1;
}
if((Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED)) == NULL) {
//if((Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_SOFTWARE)) == NULL) {
log("SDL_CreateRenderer Error");
return -1;
}
if (SDL_SetRenderDrawColor(Renderer, 0x00, 0x00, 0x00, 0xFF) != 0)
{
log("SDL_SetRenderDrawColor Error");
return -1;
}
if ( (Texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, WindowWidth, WindowHeight)) == NULL )
{
log("SDL_SetRenderDrawColor Error");
return -1;
}
SDL_Event Event;
/* ******************* SDL init Audio ***************************** */
SDL_AudioSpec AudioSpec;
AudioSpec.freq = pAudioCodecCtx->sample_rate;
AudioSpec.format = AUDIO_F32;
AudioSpec.channels = pAudioCodecCtx->channels;
AudioSpec.samples = 1024;
AudioSpec.callback = NULL;
AudioSpec.userdata = NULL;
AudioSpec.silence = 0;
if( SDL_OpenAudio(&AudioSpec, NULL) != 0 )
{
log("SDL_OpenAudio Error" + std::string(SDL_GetError()));
return -1;
}
SDL_PauseAudio(0);
/* ******************* SDL init end ***************************** */
/* read data */
// long keyFrameSum = 0;
bool isSDLRunning = true;
int packetSize = 0;
//std::ofstream YUVfile2write = std::ofstream("5v.yuv",std::ios_base::binary);
while (av_read_frame(pFmtCtx, &packet) >= 0){
usleep(1);
while(SDL_PollEvent(&Event) != 0) {
usleep(1);
switch (Event.type) {
case SDL_KEYDOWN :
if (Event.key.keysym.sym != SDLK_ESCAPE) {
break;
}
case SDL_QUIT :
isSDLRunning = false;
/*************** SDL clear ****************/
if(Renderer) {
SDL_DestroyRenderer(Renderer);
Renderer = NULL;
}
if(Window) {
SDL_DestroyWindow(Window);
Window = NULL;
}
//SDL_PauseAudio(1);
// if (YUVfile2write.is_open())
// YUVfile2write.close();
SDL_Quit();
std::cout << "closed" <<std::endl;
/*************** SDL clear END ****************/
exit(1);
return -1;
break;
default :
break;
}
}
if (packet.size <= 0)
{
continue;
}
packetSize += packet.size;
if (packet.stream_index == vStreamIndex)
{
std::cout << "video ++++++++ " << pCodec->name << " packet.size " << packet.size << "Frame frame of " << pCodecCtx->frame_number << std::endl;
++i;
int ret = -1;
ret = avcodec_send_packet(pCodecCtx, &packet);
if (ret == AVERROR_EOF|| ret == AVERROR(EAGAIN) || ret == AVERROR(EINVAL) || ret == AVERROR(ENOMEM) ){
break;
}else if (ret < 0 )
{
std::cout << argv[1] << " avcodec_send_packet 1 error " << ret << std::endl;
sws_freeContext(swsCtx);
av_frame_free(&frameRGB);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
while (ret >= 0)
{
ret = avcodec_receive_frame(pCodecCtx, pFrame);
if ( ret == AVERROR_EOF || ret == AVERROR(EAGAIN) || ret == AVERROR(EINVAL) || ret == AVERROR(ENOMEM)) {
break;
} else if (ret < 0){
std::cout << argv[1] << " avcodec_receive_frame 1 error " << ret << std::endl;
sws_freeContext(swsCtx);
av_frame_free(&frameRGB);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFmtCtx);
break;
}
//if (ret >= 0)
{
//for(int i = 0; i < pFrame->height; i++)
//YUVfile2write.write((char*)(pFrame->data[0] + i * (pFrame->linesize[0])), pFrame->width);
//int UVheight = pFrame->height/2;
// for(int i = 0; i < UVheight; i++)
{
;
//YUVfile2write.write((char*)(pFrame->data[1] + i * (pFrame->linesize[1])), pFrame->linesize[1]);
//YUVfile2write.write((char*)(pFrame->data[2] + i * (pFrame->linesize[2])), pFrame->linesize[2]);
}
// std::cout << " pFrame->pkt_size:" << pFrame->pkt_size << " Y width:" << pFrame->linesize[0] << " U width:" << pFrame->linesize[1] << " V width:" << pFrame->linesize[2] <<
// " UVheight:"<<UVheight<<" pFrame->width*pFrame->height:"<<pFrame->width*pFrame->height<< " pFrame->linesize[1]*UVheight*2:"<<pFrame->linesize[1]*UVheight*2<< std::endl;
}
if ( false && i%100 == 0)
{
sws_scale(swsCtx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, frameRGB->data, frameRGB->linesize);
SaveFrame2PPM(frameRGB, pCodecCtx->width, pCodecCtx->height, i);
std::cout << "recv frame of " << i << " ret:" << ret <<std::endl;
}
//if (pFrame->key_frame == 1)
{
// keyFrameSum++;
// std::cout << "Frame frame of " << pCodecCtx->frame_number << " keyFrameSum:"<< keyFrameSum << " pict_type(1:I,2:P,3:B):" << pFrame->pict_type << " pts:" << pFrame->pts << " gopOfAVSize: " << packetSize/1024 << " KB/s" << std::endl;
packetSize = 0;
}
if (isSDLRunning)
{
SDL_UpdateYUVTexture(Texture, NULL, pFrame->data[0], pFrame->linesize[0] ,pFrame->data[1] ,pFrame->linesize[1], pFrame->data[2] ,pFrame->linesize[2]);
Rect.x = 0;
Rect.y = 0;
Rect.w = WindowWidth;
Rect.h = WindowHeight;
SDL_RenderClear(Renderer);
SDL_RenderCopy(Renderer, Texture, NULL, &Rect);
SDL_RenderPresent(Renderer);
SDL_Delay(10);
}
av_frame_unref(pFrame);
}
}
if (packet.stream_index == aStreamIndex)
{
std::cout << "audio -------- " << pAudioCodec->name << " packet.size " << packet.size <<std::endl;
int ret = -1;
ret = avcodec_send_packet(pAudioCodecCtx, &packet);
if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN) /*|| ret == AVERROR(EINVAL) || ret == AVERROR(ENOMEM) */){
break;
} else if (ret < 0 ) {
std::cout << argv[1] << " avcodec_send_packet 2 error " << ret << std::endl;
sws_freeContext(swsCtx);
av_frame_free(&frameRGB);
av_frame_free(&pFrame);
avcodec_free_context(&pAudioCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
while( ret >= 0 )
{
ret = avcodec_receive_frame(pAudioCodecCtx, pFrame);
if ( ret == AVERROR_EOF || ret == AVERROR(EAGAIN) /*|| ret == AVERROR(EINVAL) || ret == AVERROR(ENOMEM) */){
break;
} else if( ret < 0 ) {
std::cout << argv[1] << " avcodec_receive_frame 2 error " << ret << std::endl;
sws_freeContext(swsCtx);
av_frame_free(&frameRGB);
av_frame_free(&pFrame);
avcodec_free_context(&pAudioCodecCtx);
avformat_close_input(&pFmtCtx);
return -1;
}
if (isSDLRunning)
{
}
//std::cout << "audio *** " << "sample rate:" << pAudioCodecCtx->sample_rate << " codecName:" << pAudioCodec->name << " channls:" << pAudioCodecCtx->channels << " deep:" << *pAudioCodec->sample_fmts << std::endl;
if (AV_SAMPLE_FMT_FLTP == *pAudioCodec->sample_fmts){
// std::cout << "sample_fmts: AV_SAMPLE_FMT_FLTP" << std::endl;
}
av_frame_unref(pFrame);
}
}
av_packet_unref(&packet);
}
std::cout << "video *** width:" << pCodecCtx->width << " height:" << pCodecCtx->height << " codecName:" << pCodec->name << std::endl;
std::cout << argv[1] <<" open File OK! audio: " << aStreamIndex << " video: " << vStreamIndex << " | note! [ -1:not found ]" << std::endl;
if ( vStreamIndex >= 0 )
{
av_frame_unref(frameRGB);
av_frame_free(&frameRGB);
av_frame_free(&pFrame);
avcodec_free_context(&pCodecCtx);
}
avformat_close_input(&pFmtCtx);
//if (YUVfile2write.is_open())
// YUVfile2write.close();
return 0;
}
SDL2 + FFMPEG 播放器(无音频播放)
最新推荐文章于 2023-05-26 13:51:26 发布