diff --git a/cmake/FindAVCODEC.cmake b/cmake/FindAVCODEC.cmake new file mode 100644 index 00000000..4cbe0fc7 --- /dev/null +++ b/cmake/FindAVCODEC.cmake @@ -0,0 +1,12 @@ +find_path(AVCODEC_INCLUDE_DIR + NAMES libavcodec/avcodec.h) + +find_library(AVCODEC_LIBRARY + NAMES avcodec) + +set(AVCODEC_LIBRARIES ${AVCODEC_LIBRARY}) +set(AVCODEC_INCLUDE_DIRS ${AVCODEC_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(AVCODEC DEFAULT_MSG AVCODEC_LIBRARY AVCODEC_INCLUDE_DIR) diff --git a/cmake/FindAVUTIL.cmake b/cmake/FindAVUTIL.cmake new file mode 100644 index 00000000..dcf118ed --- /dev/null +++ b/cmake/FindAVUTIL.cmake @@ -0,0 +1,12 @@ +find_path(AVUTIL_INCLUDE_DIR + NAMES libavutil/avutil.h) + +find_library(AVUTIL_LIBRARY + NAMES avutil) + +set(AVUTIL_LIBRARIES ${AVUTIL_LIBRARY}) +set(AVUTIL_INCLUDE_DIRS ${AVUTIL_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(AVUTIL DEFAULT_MSG AVUTIL_LIBRARY AVUTIL_INCLUDE_DIR) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ee3ab0b7..f018a86c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,35 @@ +#查找SDL是否安装 +find_package(SDL QUIET) +if(SDL_FOUND) +include_directories(${SDL_INCLUDE_DIR}) +message(STATUS "找到SDL") +endif(SDL_FOUND) + +#查找ffmpeg/libutil是否安装 +find_package(AVUTIL QUIET) +if(AVUTIL_FOUND) +include_directories(${AVUTIL_INCLUDE_DIR}) +message(STATUS "找到libutil") +endif(AVUTIL_FOUND) + +#查找ffmpeg/libavcodec是否安装 +find_package(AVCODEC QUIET) +if(AVCODEC_FOUND) +include_directories(${AVCODEC_INCLUDE_DIR}) +message(STATUS "找到libavcodec") +endif(AVCODEC_FOUND) + + aux_source_directory(. TEST_SRC_LIST) + +#如果ffmpeg/libavcodec ffmpeg/libavcodec SDL 都安装了则编译 test_player +if(SDL_FOUND AND AVCODEC_FOUND AND AVUTIL_FOUND) +message(STATUS "test_player被编译") +else(SDL_FOUND AND AVCODEC_FOUND AND AVUTIL_FOUND) +message(STATUS "test_player被忽略,如需编译,请先安装sdl ffmpeg/libavcodec ffmpeg/libavcodec") +list(REMOVE_ITEM TEST_SRC_LIST ./test_player.cpp) +endif(SDL_FOUND AND AVCODEC_FOUND AND AVUTIL_FOUND) + foreach(TEST_SRC ${TEST_SRC_LIST}) STRING(REGEX REPLACE ".cpp" "" TEST_EXE_NAME ${TEST_SRC}) STRING(REGEX REPLACE "./" "" TEST_EXE_NAME ${TEST_EXE_NAME}) @@ -36,4 +67,23 @@ if(MYSQL_FOUND) target_link_libraries(${TEST_EXE_NAME} mysqlclient) endif(MYSQL_FOUND) +#link test_player +if(TEST_EXE_NAME STREQUAL test_player) +target_link_libraries(${TEST_EXE_NAME} SDL avcodec avutil) +endif(TEST_EXE_NAME STREQUAL test_player) + endforeach(TEST_SRC ${TEST_SRC_LIST}) + + + + + + + + + + + + + + diff --git a/tests/H264Decoder.h b/tests/H264Decoder.h new file mode 100644 index 00000000..354f55dd --- /dev/null +++ b/tests/H264Decoder.h @@ -0,0 +1,84 @@ +/* + * H264Decoder.h + * + * Created on: 2016年12月21日 + * Author: xzl + */ + +#ifndef H264Decoder_H_ +#define H264Decoder_H_ +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +//#include "libavutil/mathematics.h" +#include "libavcodec/avcodec.h" +//#include "libswscale/swscale.h" +#ifdef __cplusplus +} +#endif + +using namespace std; + +namespace ZL { +namespace Codec { + +class H264Decoder +{ +public: + H264Decoder(void){ + avcodec_register_all(); + AVCodec *pCodec = avcodec_find_decoder(CODEC_ID_H264); + if (!pCodec) { + throw std::runtime_error("未找到H264解码器"); + } + m_pContext.reset(avcodec_alloc_context3(pCodec), [](AVCodecContext *pCtx) { + avcodec_close(pCtx); + avcodec_free_context(&pCtx); + }); + if (!m_pContext) { + throw std::runtime_error("创建解码器失败"); + } + if (pCodec->capabilities & CODEC_CAP_TRUNCATED) { + /* we do not send complete frames */ + m_pContext->flags |= CODEC_FLAG_TRUNCATED; + } + if(avcodec_open2(m_pContext.get(), pCodec, NULL)< 0){ + throw std::runtime_error("打开编码器失败"); + } + m_pFrame.reset(av_frame_alloc(),[](AVFrame *pFrame){ + av_frame_free(&pFrame); + }); + if (!m_pFrame) { + throw std::runtime_error("创建帧缓存失败"); + } + } + virtual ~H264Decoder(void){} + bool inputVideo(unsigned char* data,unsigned int dataSize,uint32_t ui32Stamp,AVFrame **ppFrame){ + AVPacket pkt; + av_init_packet(&pkt); + pkt.data = data; + pkt.size = dataSize; + pkt.dts = ui32Stamp; + int iGotPicture ; + auto iLen = avcodec_decode_video2(m_pContext.get(), m_pFrame.get(), &iGotPicture, &pkt); + if (!iGotPicture || iLen < 0) { + return false; + } + *ppFrame = m_pFrame.get(); + return true; + } +private: + std::shared_ptr m_pContext; + std::shared_ptr m_pFrame; +}; + + +} /* namespace Codec */ +} /* namespace ZL */ + +#endif /* H264Decoder_H_ */ + + diff --git a/tests/YuvDisplayer.h b/tests/YuvDisplayer.h new file mode 100644 index 00000000..f1dc452d --- /dev/null +++ b/tests/YuvDisplayer.h @@ -0,0 +1,79 @@ +/* + * YuvDisplayer.h + * + * Created on: 2016年12月21日 + * Author: xzl + */ + +#ifndef YUVDISPLAYER_H_ +#define YUVDISPLAYER_H_ +#include +#include "Util/onceToken.h" +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#ifdef __cplusplus +} +#endif + +namespace ZL { +namespace Screen { + +class YuvDisplayer { +public: + YuvDisplayer(){ + static onceToken token([]() { + if(SDL_Init(SDL_INIT_EVERYTHING) == -1) { + throw std::runtime_error("初始化SDL失败"); + } + }, []() { + SDL_Quit(); + }); + } + virtual ~YuvDisplayer(){ + if(m_pOverlay){ + SDL_FreeYUVOverlay(m_pOverlay); + } + } + bool displayYUV(AVFrame *pFrame){ + if (!m_pScreen) { + /* Set up the screen */ + m_pScreen = SDL_SetVideoMode(1366, 768, 16, SDL_SWSURFACE); + } + + if (!m_pOverlay && m_pScreen) { + /* Create a YUV overlay */ + m_pOverlay = SDL_CreateYUVOverlay(pFrame->width, pFrame->height, SDL_YV12_OVERLAY, m_pScreen); + /* Set the window caption */ + SDL_WM_SetCaption("YUV Window", NULL); + } + if (m_pOverlay) { + /* Apply the image to the screen */ + m_pOverlay->pixels[0] = pFrame->data[0]; + m_pOverlay->pixels[2] = pFrame->data[1]; + m_pOverlay->pixels[1] = pFrame->data[2]; + + m_pOverlay->pitches[0] = pFrame->linesize[0]; + m_pOverlay->pitches[2] = pFrame->linesize[1]; + m_pOverlay->pitches[1] = pFrame->linesize[2]; + + /* Update the screen */ + SDL_Rect rect = { 0 ,0 ,1366,768}; + SDL_LockYUVOverlay(m_pOverlay); + SDL_DisplayYUVOverlay(m_pOverlay, &rect); + SDL_UnlockYUVOverlay(m_pOverlay); + return true; + } + return false; + } +private: + SDL_Surface* m_pScreen = nullptr; + SDL_Overlay* m_pOverlay = nullptr; +}; + +} /* namespace Screen */ +} /* namespace ZL */ + +#endif /* YUVDISPLAYER_H_ */ diff --git a/tests/test_player.cpp b/tests/test_player.cpp new file mode 100644 index 00000000..173a2190 --- /dev/null +++ b/tests/test_player.cpp @@ -0,0 +1,78 @@ + +#include +#include +#include "Util/logger.h" +#include +#include "Poller/EventPoller.h" +#include "Rtsp/UDPServer.h" +#include "Util/onceToken.h" +#include "Device/PlayerProxy.h" +#include "H264Decoder.h" +#include "YuvDisplayer.h" +#include "Network/sockutil.h" + +using namespace std; +using namespace ZL::Screen; +using namespace ZL::Codec; +using namespace ZL::Util; +using namespace ZL::Thread; +using namespace ZL::Network; +using namespace ZL::Rtsp; +using namespace ZL::DEV; + +void programExit(int arg) { + EventPoller::Instance().shutdown(); +} + +int main(int argc, char *argv[]){ + + Logger::Instance().add(std::make_shared("stdout", LTrace)); + //Logger::Instance().setWriter(std::make_shared()); + signal(SIGINT, programExit); + + if(argc != 5){ + FatalL << "\r\n测试方法:./test_player rtxp_url rtsp_user rtsp_pwd rtp_type\r\n" + << "例如:./test_player rtsp://127.0.0.1/live/0 admin 123456 0\r\n" + <setOnPlayResult([](const SockException &ex) { + InfoL << "OnPlayResult:" << ex.what(); + }); + player->setOnShutdown([](const SockException &ex) { + ErrorL << "OnShutdown:" << ex.what(); + }); + + //DebugL << argv[1] << " " << argv[2] << " " << argv[3] << " " << argv[4] << endl; + player->play(argv[1],argv[2],argv[3],(PlayerBase::eRtpType)atoi(argv[4])); + + H264Decoder decoder; + YuvDisplayer displayer; + ThreadPool pool(1); + player->setOnVideoCB([&](const H264Frame &frame){ + pool.async([&,frame]() { + AVFrame *pFrame = nullptr; + bool flag = decoder.inputVideo((unsigned char *)frame.data.data(), frame.data.size() ,frame.timeStamp, &pFrame); + if(flag) { + //DebugL << pFrame->pkt_pts; + displayer.displayYUV(pFrame); + } + }); + }); + + EventPoller::Instance().runLoop(); + + + static onceToken token(nullptr, []() { + UDPServer::Destory(); + AsyncTaskThread::Destory(); + EventPoller::Destory(); + Logger::Destory(); + }); + return 0; +} +