|
@@ -0,0 +1,239 @@
|
|
|
+/**
|
|
|
+ * 叶海辉
|
|
|
+ * QQ群121376426
|
|
|
+ * http://blog.yundiantech.com/
|
|
|
+ */
|
|
|
+
|
|
|
+#include <thread>
|
|
|
+
|
|
|
+#include "FFmpegVideoParsing.h"
|
|
|
+
|
|
|
+#if defined(WIN32)
|
|
|
+#include <WinSock2.h>
|
|
|
+#include <Windows.h>
|
|
|
+#include <direct.h>
|
|
|
+#include <io.h> //C (Windows) access
|
|
|
+#else
|
|
|
+#include <sys/time.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <unistd.h>
|
|
|
+
|
|
|
+void Sleep(long mSeconds)
|
|
|
+{
|
|
|
+ usleep(mSeconds * 1000);
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+#include <QDebug>
|
|
|
+#include "AppConfig.h"
|
|
|
+
|
|
|
+int read_buffer(void *opaque, uint8_t *buf, int buf_size)
|
|
|
+{
|
|
|
+ FFmpegVideoParsing *pointer = (FFmpegVideoParsing*)opaque;
|
|
|
+
|
|
|
+ int readsize = pointer->readVideoBuffer(buf, buf_size);
|
|
|
+//fprintf(stderr, "%s %d readsize=%d \n", __FUNCTION__, buf_size, readsize);
|
|
|
+ if(readsize > 0)
|
|
|
+ {
|
|
|
+ return readsize;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return AVERROR_EOF;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int MAX_FRAME_SIZE = 1024 * 1024 * 10;
|
|
|
+
|
|
|
+FFmpegVideoParsing::FFmpegVideoParsing()
|
|
|
+{
|
|
|
+ mCallBackFunc = nullptr;
|
|
|
+ mCallBackFuncParam = nullptr;
|
|
|
+
|
|
|
+ mCond = new Cond();
|
|
|
+ mVideoBuffer = (uint8_t*)malloc(MAX_FRAME_SIZE);
|
|
|
+ mVideoBufferSize = 0;
|
|
|
+}
|
|
|
+
|
|
|
+FFmpegVideoParsing::~FFmpegVideoParsing()
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+void FFmpegVideoParsing::start(std::function<void (uint8_t *buffer, const int &size, void *param)> callBackFunc, void *callBackFuncParam)
|
|
|
+{
|
|
|
+ mIsStop = false;
|
|
|
+ mCallBackFunc = callBackFunc;
|
|
|
+ mCallBackFuncParam = callBackFuncParam;
|
|
|
+
|
|
|
+ mVideoBufferSize = 0;
|
|
|
+
|
|
|
+ //启动新的线程实现读取视频文件
|
|
|
+ std::thread([=]
|
|
|
+ {
|
|
|
+ this->run();
|
|
|
+
|
|
|
+ }).detach();
|
|
|
+}
|
|
|
+
|
|
|
+void FFmpegVideoParsing::stop()
|
|
|
+{
|
|
|
+ mIsStop = true;
|
|
|
+
|
|
|
+ while (mIsThreadRunning)
|
|
|
+ {
|
|
|
+ AppConfig::mSleep(100);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int FFmpegVideoParsing::inputVideoBuffer(const uint8_t * buffer, const int &size)
|
|
|
+{
|
|
|
+ mCond->Lock();
|
|
|
+
|
|
|
+ while ((mVideoBufferSize + size) > MAX_FRAME_SIZE)
|
|
|
+ {
|
|
|
+ mCond->Wait(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(mVideoBuffer + mVideoBufferSize, buffer, size);
|
|
|
+ mVideoBufferSize += size;
|
|
|
+ int totalSize = mVideoBufferSize;
|
|
|
+ mCond->Unlock();
|
|
|
+
|
|
|
+ return totalSize;
|
|
|
+}
|
|
|
+
|
|
|
+int FFmpegVideoParsing::readVideoBuffer(uint8_t * bufferOut, const int &size)
|
|
|
+{
|
|
|
+ mCond->Lock();
|
|
|
+
|
|
|
+ while (mVideoBufferSize <= 0)
|
|
|
+ {
|
|
|
+ if (mIsStop)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ mCond->Wait(100);
|
|
|
+ }
|
|
|
+
|
|
|
+ int bufSize = 0;
|
|
|
+
|
|
|
+ if (mVideoBufferSize > 0)
|
|
|
+ {
|
|
|
+ bufSize = mVideoBufferSize > size ? size : mVideoBufferSize;
|
|
|
+ memcpy(bufferOut, mVideoBuffer, bufSize);
|
|
|
+
|
|
|
+ /// 把后面的数据覆盖上来
|
|
|
+ int leftSize = mVideoBufferSize - bufSize;
|
|
|
+ memmove(mVideoBuffer, mVideoBuffer + bufSize, leftSize);
|
|
|
+ mVideoBufferSize = leftSize;
|
|
|
+ }
|
|
|
+
|
|
|
+ mCond->Unlock();
|
|
|
+
|
|
|
+ return bufSize;
|
|
|
+}
|
|
|
+
|
|
|
+void FFmpegVideoParsing::run()
|
|
|
+{
|
|
|
+ mIsThreadRunning = true;
|
|
|
+
|
|
|
+ #define IO_CTX_BUFFER_SIZE 4096 * 4
|
|
|
+
|
|
|
+ unsigned char *aviobuffer=(unsigned char *)av_malloc(IO_CTX_BUFFER_SIZE);//申请自定义IO的缓冲区,必备,4k对齐
|
|
|
+ AVIOContext *pIOCtx = avio_alloc_context(aviobuffer, IO_CTX_BUFFER_SIZE, 0, this, read_buffer, NULL, NULL);//opaque为NULL即可
|
|
|
+
|
|
|
+ AVFormatContext *pFormatCtx = nullptr;
|
|
|
+
|
|
|
+do
|
|
|
+{
|
|
|
+
|
|
|
+ AVInputFormat *piFmt = NULL;
|
|
|
+
|
|
|
+ //step2:探测流格式
|
|
|
+ int ret = av_probe_input_buffer(pIOCtx, &piFmt, NULL, NULL, 0, 0);
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ {
|
|
|
+ fprintf(stderr, "probe failed!\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ fprintf(stdout, "probe success!\n");
|
|
|
+ fprintf(stdout, "format: %s[%s]\n", piFmt->name, piFmt->long_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ pFormatCtx = avformat_alloc_context();//建立fmt_ctx格式上下文(统领全文的结构体)
|
|
|
+
|
|
|
+ // Set the IOContext:
|
|
|
+ pFormatCtx->pb = pIOCtx; //关键赋值 ,将自定义的IO赋予fmt_ctx接口
|
|
|
+ pFormatCtx->flags = AVFMT_FLAG_CUSTOM_IO;
|
|
|
+
|
|
|
+
|
|
|
+ //step4:打开流
|
|
|
+ if (avformat_open_input(&pFormatCtx, "", piFmt, NULL) < 0)
|
|
|
+ {
|
|
|
+ fprintf(stderr, "avformat open failed.\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ fprintf(stdout, "open stream success!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (avformat_find_stream_info(pFormatCtx, NULL)<0)
|
|
|
+ {
|
|
|
+ fprintf(stdout, "av_find_stream_info error \n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+// if (ret != 0)
|
|
|
+// {
|
|
|
+// char buffer[1024] = {0};
|
|
|
+// av_strerror(ret, buffer, 1024);
|
|
|
+// fprintf(stderr, "avformat_open_input ret=%d %s \n", ret, buffer);
|
|
|
+// }
|
|
|
+
|
|
|
+ fprintf(stderr, "... %d \n", ret);
|
|
|
+
|
|
|
+// while (1)
|
|
|
+// {
|
|
|
+// AVPacket packet;
|
|
|
+// int ret = av_read_frame(pFormatCtx, &packet);
|
|
|
+
|
|
|
+// if (ret != 0 && mIsStop)
|
|
|
+// {
|
|
|
+// break;
|
|
|
+// }
|
|
|
+
|
|
|
+// if (mCallBackFunc != nullptr)
|
|
|
+// {
|
|
|
+// mCallBackFunc((uint8_t*)packet.data, packet.size, mCallBackFuncParam);
|
|
|
+// }
|
|
|
+
|
|
|
+// av_packet_unref(&packet);
|
|
|
+// }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}while(0);
|
|
|
+
|
|
|
+ av_free(pIOCtx->buffer);
|
|
|
+
|
|
|
+ avio_context_free(&pIOCtx);
|
|
|
+
|
|
|
+ if (pFormatCtx != nullptr)
|
|
|
+ {
|
|
|
+ avformat_close_input(&pFormatCtx);
|
|
|
+ avformat_free_context(pFormatCtx);
|
|
|
+ }
|
|
|
+
|
|
|
+ mIsThreadRunning = false;
|
|
|
+
|
|
|
+ fprintf(stdout, " read finished!");
|
|
|
+
|
|
|
+}
|
|
|
+
|