#include <SDKDDKVer.h> #include <windows.h> #include "main.h" #include "rtsp.h" #include "lock.h" #include <sys/types.h> #include <sys/stat.h> extern "C" { #include <libavformat/avformat.h> #include <libavdevice/avdevice.h> #include <libswscale/swscale.h> #include <libavcodec/avcodec.h> } #ifdef __cplusplus #define T LpRtsp_T #else #define T Rtsp_T #endif typedef int(*tdf_framestream)(unsigned long fmid, unsigned char* frame[], unsigned char* info[], void*ctx); typedef int(*tdf_framefilter)(unsigned long fmid, unsigned char* frame[], unsigned char* info[], void*ctx); typedef int(*tdf_framereport)(unsigned long fmid, unsigned char* frame[], unsigned char* info[], void*ctx); struct Rtsp_T { AVFormatContext * format_context; AVInputFormat * input_format; AVDictionary * dictionary; AVCodecContext * codec_context; AVCodec * codec; AVPacket * packet; SwsContext * sws_context; AVFrame * frame; AVFrame * yuv; uint8_t * buffer; HANDLE event; HANDLE estop; int width; int height; int frame_size; int fps; int iindex; int isopen; const char* uri; const char* winname; const char* libname; void*ptr; void*ctx; void(*print)(void*ctx, unsigned char* frame, int width, int height, int nbits); volatile long exit; PTP_WORK pwk_stream; PTP_WORK pwk_filter; PTP_WORK pwk_report; tdf_framestream * pfstream; tdf_framefilter * pffilter; tdf_framereport * pfreport; LpLock_T * vlock; int numlock; long ref_signal; static long ref_count; #ifdef _DEBUG long report_count; long filter_count; long stream_count; #endif }; long Rtsp_T::ref_count = 0; int Rtsp_isoff(T rt) { return rt->exit == 1; } void Rtsp_close(T rt) { if (!rt->isopen) { return; } if (rt->sws_context) { sws_freeContext(rt->sws_context); rt->sws_context = nullptr; } if (rt->frame) { av_free(rt->frame); rt->frame = nullptr; } if (rt->codec_context) { avcodec_close(rt->codec_context); rt->codec_context = nullptr; } if (rt->format_context) { avformat_close_input(&rt->format_context); rt->format_context = nullptr; } rt->fps = -1; rt->isopen = 0; } int Rtsp_open(T rt, const char* uri) { int ret = -1; if (rt->isopen) { return ret; } if (strcmp("file:///", uri) <= 0) { } else if (strcmp("rtsp://", uri) <= 0) { if (0 > (ret = av_dict_set(&rt->dictionary, "rtsp_transport", "tcp", 0))) { TRACE_LOG(" av_dict_set rtsp_transport failed"); return ret; } } else { uri = nullptr; rt->input_format = av_find_input_format("vfwcap"); } if (0 > (ret = av_dict_set(&rt->dictionary, "stimeout", "6000000", 0))) { TRACE_LOG("av_dict_set stimeout failed"); } else if (0 > (ret = av_dict_set(&rt->dictionary, "list_devices", "true", 0))) { TRACE_LOG("av_dict_set list_devices failed"); } else if (0 != (ret = avformat_open_input(&rt->format_context, uri, rt->input_format, &rt->dictionary))) { TRACE_LOG("avformat_open_input failed, ret = %d", ret); } else if (0 > (ret = avformat_find_stream_info(rt->format_context, nullptr))) { TRACE_LOG("avformat_find_stream_info failed, ret = %d", ret); } else { unsigned int i; for (i = 0; i < rt->format_context->nb_streams; ++i) { if (rt->format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { break; } } if (rt->format_context->nb_streams == i) { TRACE_LOG("find video stream failed"); ret = -1; } else { rt->iindex = i; rt->fps = rt->format_context->streams[i]->r_frame_rate.num / rt->format_context->streams[i]->r_frame_rate.den; rt->codec_context = rt->format_context->streams[i]->codec; rt->codec = avcodec_find_decoder(rt->codec_context->codec_id); if (rt->codec == NULL) { TRACE_LOG("find video stream failed"); ret = -1; } else if (0 > (ret = avcodec_open2(rt->codec_context, rt->codec, NULL))) { TRACE_LOG("avcodec_open2 failed"); ret = -1; } else { rt->width = rt->codec_context->width; rt->height = rt->codec_context->height; rt->frame_size = rt->width * rt->height; rt->sws_context = sws_getContext(rt->width , rt->height , rt->codec_context->pix_fmt , rt->width , rt->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); rt->frame = av_frame_alloc(); ret = 0; rt->isopen = 1; } } } return ret; } int def_framestream(unsigned long fmid, unsigned char* frame[], unsigned char* info[], void*ctx) { T rt = (T)ctx; int retv = (-1); if (!rt->exit) { if (av_read_frame(rt->format_context, rt->packet) < 0) { TRACE_LOG("av_read_frame failed"); } else { if (nullptr == *frame) { *frame = (unsigned char*)malloc(rt->frame_size); } #ifdef _DEBUG long ref_count; if (0 == ( (ref_count = InterlockedIncrement(&rt->stream_count)) & REF_COUNT)) printf("stream frame..................................................%d \r\n", ref_count); #endif retv = 0; } } return retv; } int def_framefilter(unsigned long fmid, unsigned char* frame[], unsigned char* info[], void*ctx) { T rt = (T)ctx; int gotptr ,retv = (-1); if (rt->packet->stream_index == rt->iindex) { if (avcodec_decode_video2(rt->codec_context, rt->frame, &gotptr, rt->packet) < 0) { TRACE_LOG(" avcodec_decode_video2 failed"); } else if (gotptr) { sws_scale (rt->sws_context , rt->frame->data , rt->frame->linesize , 0 , rt->height , rt->yuv->data , rt->yuv->linesize); assert(frame); memcpy_s(*frame, rt->frame_size, rt->yuv->data[0], rt->frame_size); #ifdef _DEBUG long ref_count; if (0 == ((ref_count = InterlockedIncrement(&rt->filter_count)) & REF_COUNT)) printf("filter frame..................................................%d \r\n", ref_count); #endif retv = 0; } } return retv; } int def_framereport(unsigned long fmid, unsigned char** frame, unsigned char* info[], void*ctx) { T rt = (T)ctx; #ifdef _DEBUG long ref_count; if( 0 ==((ref_count = InterlockedIncrement(&rt->report_count)) & REF_COUNT)) printf("report frame..................................................%d \r\n", ref_count); #endif rt->print(rt->ctx, *frame, rt->width, rt->height, 8); *frame = nullptr; return 0; } static VOID __stdcall __TrySubmitReportFrame(PTP_CALLBACK_INSTANCE instance, PVOID pv, PTP_WORK pwk) { T ap = (T)pv; unsigned char*ptr = nullptr; int i, e = ap->numlock - 1; while (0 == Lock_get(ap->vlock[e - 1], (void**)&ptr, 0)) {/* 有数据就一直读 */ for (i = 0; ap->pfreport[i]; ++i) { ap->pfreport[i](0, &ptr, nullptr, ap->ptr); } if (0 != Lock_put(ap->vlock[e], ptr, 0)) { free(ptr); #ifdef _DEBUG printf(" __TrySubmitReportFrame :Lock_put:timeout. \r\n "); #endif } ptr = nullptr; } } static VOID __stdcall __TrySubmitFilterFrame(PTP_CALLBACK_INSTANCE instance, PVOID pv, PTP_WORK pwk) { T ap = (T)pv; unsigned char*ptr = nullptr; int i; for (i = 0; ap->pffilter[i]; ) { if (0 != Lock_get(ap->vlock[i], (void**)&ptr, 0)) { ++i; continue; // 队列为空,暂时退出 } if (0 !=ap->pffilter[i](0, &ptr, nullptr, ap->ptr) || 0 != Lock_put(ap->vlock[i + 1], ptr, 400)) { free(ptr); #ifdef _DEBUG printf(" __TrySubmitFilterFrame :Lock_put:timeout. \r\n "); #endif } else if (nullptr == ap->pffilter[i + 1]) { SubmitThreadpoolWork(ap->pwk_report); } ptr = nullptr; } // for } static VOID __stdcall __TrySubmitStreamFrame(PTP_CALLBACK_INSTANCE instance, PVOID pv, PTP_WORK pwk) { T ap = (T)pv; unsigned char*ptr = nullptr; int e = ap->numlock - 1; int i; for (i = 0; ap->pfstream[i]; ++i) { if (nullptr == ptr) { Lock_get(ap->vlock[e], (void**)&ptr, 0); } else { #ifdef _DEBUG printf(" __TrySubmitStreamFrame :Lock_put:timeout. \r\n "); #endif } if (0 != ap->pfstream[i](0, &ptr, nullptr, ap->ptr)) { if (InterlockedDecrementAcquire(&ap->ref_signal) <= 0) { printf("__TrySubmitStreamFrame(ap) : SetEvent. \r\n"); SetEventWhenCallbackReturns(instance, ap->event); } break; } if (0 == Lock_put(ap->vlock[0], ptr, 700)) { SubmitThreadpoolWork(ap->pwk_filter); ptr = nullptr; } if (nullptr == ap->pfstream[i + 1]) { SubmitThreadpoolWork(pwk); } } if (nullptr != ptr) { free(ptr); } } static VOID __stdcall __TrySubmitApplyFrame(PTP_CALLBACK_INSTANCE instance, PVOID pv) { T ap = (T)pv; int i; ap->packet = av_packet_alloc(); ap->yuv = av_frame_alloc(); ap->buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, ap->width, ap->height)); avpicture_fill((AVPicture *)ap->yuv, ap->buffer, AV_PIX_FMT_YUV420P, ap->width, ap->height); ResetEvent(ap->estop); #ifdef _DEBUG ap->report_count = 0L; ap->filter_count = 0L; ap->stream_count = 0L; #endif for (i = 0; ap->pfstream[i]; ++i) { SubmitThreadpoolWork(ap->pwk_stream); } #ifdef _DEBUG printf("__TrySubmitApplyThread(ap) : Wait . \r\n"); #endif CallbackMayRunLong(instance); WaitForSingleObject(ap->event, INFINITE); { unsigned char *ptr = nullptr; for (i = 0; i < ap->numlock;) { if (Lock_get(ap->vlock[i],(void**)&ptr, 700) == 0) free(ptr); else ++i; } } #ifdef _DEBUG printf("__TrySubmitApplyThread(ap) : Free . \r\n"); #endif av_frame_free(&ap->yuv); av_free(ap->buffer); av_free_packet(ap->packet); free(ap->pfstream); free(ap->pffilter); free(ap->pfreport); for (; ap->numlock-- > 0; ) { Lock_free(&ap->vlock[ap->numlock]); } free(ap->vlock); SetEvent(ap->estop); #ifdef _DEBUG printf("__TrySubmitApplyThread(ap) : Exit . \r\n"); #endif } static void Rtsp_apply(T ap, tdf_framestream stream[] = nullptr, tdf_framefilter filter[] = nullptr , tdf_framereport report[] = nullptr) { int i, n = 0; if (!stream || !stream[0]) { i = 1; ap->pfstream = (tdf_framestream *)malloc((i + 1) * sizeof(*ap->pfstream)); ap->pfstream[0] = def_framestream; } else { for (i = 0; stream[i]; ++i); ap->pfstream = (tdf_framestream *)malloc((i + 1) * sizeof(*ap->pfstream)); for (i = 0; stream[i]; ++i)ap->pfstream[i] = stream[i]; } InterlockedExchangeAcquire(&ap->ref_signal,i); ap->pfstream[i] = nullptr; n += 1; // if (!filter || !filter[0]) { i = 1; ap->pffilter = (tdf_framefilter*)malloc((i + 1) * sizeof(*ap->pffilter)); ap->pffilter[0] = def_framefilter; } else { for (i = 0; filter[i]; ++i); ap->pffilter = (tdf_framestream *)malloc((i + 1) * sizeof(*ap->pffilter)); for (i = 0; filter[i]; ++i)ap->pffilter[i] = filter[i]; } ap->pffilter[i] = nullptr; n += i; // if (!report || !report[0]) { i = 1; ap->pfreport = (tdf_framefilter*)malloc((i + 1) * sizeof(*ap->pfreport)); ap->pfreport[0] = def_framereport; } else { for (i = 0; report[i]; ++i); ap->pfreport = (tdf_framestream *)malloc((i + 1) * sizeof(*ap->pfreport)); for (i = 0; report[i]; ++i)ap->pfreport[i] = report[i]; } ap->pfreport[i] = nullptr; n += 1; // ap->vlock = (LpLock_T *)malloc(n * sizeof *ap->vlock); for (ap->numlock = 0; ap->numlock < n; ++ap->numlock) { ap->vlock[ap->numlock] = Lock_new(3072); } TrySubmitThreadpoolCallback(__TrySubmitApplyFrame, ap, NULL); } int Rtsp_stop(T rt) { if (!rt->exit) { printf("Rtsp_stop. \r\n"); InterlockedExchangeAcquire(&rt->exit, 1); WaitForSingleObject(rt->estop, INFINITE); } return 0; } int Rtsp_start(T rt, void*ctx, void(*print)(void*ctx, unsigned char* frame, int width, int height, int nbits)) { if (!print) { return (-1); } if (rt->exit) { rt->ctx = ctx; rt->ptr = rt; rt->print = print; printf("Rtsp_start. \r\n"); InterlockedExchangeAcquire(&rt->exit, 0); Rtsp_apply(rt); } return 0; } T Rtsp_new() { LpRtsp_T rt = (LpRtsp_T)malloc(sizeof * rt); printf("Rtsp_new. \r\n"); if (1 == InterlockedIncrementAcquire(&Rtsp_T::ref_count)) { av_register_all(); avdevice_register_all(); avformat_network_init(); } rt->format_context = avformat_alloc_context(); rt->input_format = nullptr; rt->dictionary = nullptr; rt->codec_context = nullptr; rt->codec = nullptr; rt->packet = nullptr; rt->sws_context = nullptr; rt->frame = nullptr; rt->winname = nullptr; rt->uri = nullptr; rt->libname = nullptr; rt->ptr = nullptr; rt->exit = 1; rt->isopen = 0; rt->ctx = nullptr; rt->fps = -1; rt->event = CreateEvent(NULL, FALSE, FALSE, NULL); rt->estop = CreateEvent(NULL, TRUE , FALSE, NULL); rt->pwk_stream = CreateThreadpoolWork(__TrySubmitStreamFrame, rt, NULL); rt->pwk_filter = CreateThreadpoolWork(__TrySubmitFilterFrame, rt, NULL); rt->pwk_report = CreateThreadpoolWork(__TrySubmitReportFrame, rt, NULL); return rt; } void Rtsp_free(T * rt) { assert(rt&&&rt); printf("Rtsp_free. \r\n"); avformat_close_input(&(*rt)->format_context); if (0 == InterlockedDecrementAcquire(&Rtsp_T::ref_count)) { avformat_network_deinit(); } CloseHandle((*rt)->event); CloseHandle((*rt)->estop); CloseThreadpoolWork((*rt)->pwk_stream); CloseThreadpoolWork((*rt)->pwk_filter); CloseThreadpoolWork((*rt)->pwk_report); Rtsp_close(*rt); free(*rt); (*rt) = nullptr; }