/* o a se compila cu: * gcc -o sdlPlayer sdlPlayer.c -L ../build/lib -I ../build/include -I/usr/include/SDL * -lavformat -lavcodec -lavutil -lswscale -lz -lbz2 -lm -lSDL * o pt. rulare: * ./sdlPlayer cars.avi * */ #include #include #include #include #include #include #ifdef __MINGW32__ #undef main /* Prevents SDL from overriding main() */ #endif #define MAX_VIDEOQ_LENGTH 20 int quit; uint64_t global_video_pts = AV_NOPTS_VALUE; typedef struct VideoPicture { AVPicture *pict; // campul "pict" se aloca si se dealoca in afara lui VideoPictureQueue struct VideoPicture *next; double pts; // Presentation TimeStamp } VideoPicture; typedef struct VideoPictureQueue { VideoPicture *first, *last; int length; // int size; SDL_mutex *mutex; SDL_cond *cond; } VideoPictureQueue; typedef struct VideoState { AVFormatContext *pFormatCtx; AVCodecContext *pCodecCtx; AVCodec *pCodec; AVStream *videoStream; int videoStreamIndex; VideoPictureQueue *pictq; } VideoState; void vpQueue_init(VideoPictureQueue *q) { q->first = q->last = NULL; q->length = 0; q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } int vpQueue_put(VideoPictureQueue *q, AVPicture *pict, double pts) { VideoPicture *vpict; vpict = av_malloc(sizeof(VideoPicture)); if (!vpict) return -1; vpict->pict = pict; vpict->next = NULL; vpict->pts = pts; SDL_LockMutex(q->mutex); if (!q->last) q->first = vpict; else q->last->next = vpict; q->last = vpict; q->length++; SDL_CondSignal(q->cond); SDL_UnlockMutex(q->mutex); return 0; } static int vpQueue_get(VideoPictureQueue *q, AVPicture **pict, double *pts, int block) { VideoPicture *vpict; int ret; SDL_LockMutex(q->mutex); *pts = -1; for(;;) { if(quit) { ret = -1; break; } vpict = q->first; if (vpict) { q->first = vpict->next; if (!q->first) q->last = NULL; q->length--; *pict = vpict->pict; *pts = vpict->pts; av_free(vpict); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } /* These are called whenever we allocate a frame * buffer. We use this to store the global_pts in * a frame at the time it is allocated. */ int my_get_buffer(struct AVCodecContext *c, AVFrame *pic) { int ret = avcodec_default_get_buffer(c, pic); uint64_t *pts = av_malloc(sizeof(uint64_t)); *pts = global_video_pts; pic->opaque = pts; return ret; } void my_release_buffer(struct AVCodecContext *c, AVFrame *pic) { if(pic) av_freep(&pic->opaque); avcodec_default_release_buffer(c, pic); } int play(void *arg) { VideoState *vs = (VideoState *) arg; AVPicture *pict, yuvpict; int i; double frame_delay, pts; SDL_Surface *screen = SDL_SetVideoMode(vs->pCodecCtx->width, vs->pCodecCtx->height, 0, 0); if(!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); quit = 1; return -2; } SDL_Rect rect; SDL_Overlay *yuv; yuv = SDL_CreateYUVOverlay(vs->pCodecCtx->width, vs->pCodecCtx->height, SDL_YV12_OVERLAY, screen); if (!yuv) { fprintf(stderr, "SDL: could not create YUV overlay!\n"); quit = 1; return -2; } // prepare a SWSContext for frame conversion static struct SwsContext *sws_ctx; sws_ctx = sws_getContext(vs->pCodecCtx->width, vs->pCodecCtx->height, vs->pCodecCtx->pix_fmt, vs->pCodecCtx->width, vs->pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); if (!sws_ctx) { fprintf(stderr, "Could not create sws scaling context for image convertion!\n"); return -1; } frame_delay = av_q2d(vs->videoStream->codec->time_base); while (!quit && vpQueue_get(vs->pictq, &pict, &pts, 1)) { printf("am scos din coada.\n"); if (pict) { SDL_LockYUVOverlay(yuv); yuvpict.data[0] = yuv->pixels[0]; yuvpict.data[1] = yuv->pixels[2]; yuvpict.data[2] = yuv->pixels[1]; yuvpict.linesize[0] = yuv->pitches[0]; yuvpict.linesize[1] = yuv->pitches[2]; yuvpict.linesize[2] = yuv->pitches[1]; i = sws_scale(sws_ctx, pict->data, pict->linesize, 0, vs->pCodecCtx->height, yuvpict.data, yuvpict.linesize); //maparea YUV frame-ului pe YUVOverlay se poate face si copiind valorile pixelilor //cu memcpy(), dar e mai rapid daca folosesc un AVFrame a carui camp data pointeaza //spre pixelii YUVOverlay-ului (cum se face mai sus cu "pict") //i = sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, // pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); //memcpy(yuv->pixels[0], pFrameYUV->data[0], pCodecCtx->width*pCodecCtx->height); //memcpy(yuv->pixels[1], pFrameYUV->data[2], pCodecCtx->width*pCodecCtx->height/4); //memcpy(yuv->pixels[2], pFrameYUV->data[1], pCodecCtx->width*pCodecCtx->height/4); printf("result of scale: %d bytes pts=%.4f frame_delay=%.4f\n",i,pts,frame_delay); SDL_UnlockYUVOverlay(yuv); rect.x = 0; rect.y = 0; rect.w = vs->pCodecCtx->width; rect.h = vs->pCodecCtx->height; SDL_DisplayYUVOverlay(yuv, &rect); SDL_Delay(10); avpicture_free(pict); av_free(pict); pict = NULL; } SDL_Delay((int) (frame_delay*1000)); // in ms; wait for the next frame's PTS } // Free the sws context sws_freeContext(sws_ctx); return 0; } main(int argc, char* argv[]) { VideoState vs; AVFrame *pFrame; AVPacket packet; int frameFinished; int i; SDL_Thread *player_thread; double pts; // register all formats and codecs av_register_all(); // Open video file if(av_open_input_file(&vs.pFormatCtx, argv[1], NULL, 0, NULL)!=0) { fprintf(stderr, "Could not open file!\n"); return -1; // Couldn't open file } // Retrieve stream information if(av_find_stream_info(vs.pFormatCtx)<0) { fprintf(stderr, "Could not find stream information!\n"); return -1; // Couldn't find stream information } // Dump information about file onto standard error dump_format(vs.pFormatCtx, 0, argv[1], 0); // Find the first video stream vs.videoStreamIndex=-1; for(i=0; inb_streams; i++) if(vs.pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) { vs.videoStreamIndex=i; break; } if(vs.videoStreamIndex==-1) { fprintf(stderr, "No video stream found!\n"); return -1; // Didn't find a video stream } vs.videoStream = vs.pFormatCtx->streams[vs.videoStreamIndex]; // Get a pointer to the codec context for the video stream vs.pCodecCtx=vs.pFormatCtx->streams[vs.videoStreamIndex]->codec; // we use our defined functions, in order to save the correct PTS of a frame vs.pCodecCtx->get_buffer = my_get_buffer; vs.pCodecCtx->release_buffer = my_release_buffer; printf("pCodecCtx->width=%d pCodecCtx->height=%d\n", vs.pCodecCtx->width, vs.pCodecCtx->height); // Find the decoder for the video stream vs.pCodec=avcodec_find_decoder(vs.pCodecCtx->codec_id); if(vs.pCodec==NULL) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Open codec if(avcodec_open(vs.pCodecCtx, vs.pCodec)<0) { fprintf(stderr, "Could not open codec!\n"); return -1; // Could not open codec } // Hack to correct wrong frame rates that seem to be generated by some codecs if(vs.pCodecCtx->time_base.num>1000 && vs.pCodecCtx->time_base.den==1) vs.pCodecCtx->time_base.den=1000; /* alocarea spatiului pt. un frame YUV - nu mai e nevoie de ea; o las ca exemplu // Allocate an AVFrame structure AVFrame *pFrameYUV = avcodec_alloc_frame(); if (pFrameRGB==NULL) return -1; int numBytes = avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); uint8_t *buffer = malloc(numBytes); // Assign appropriate parts of buffer to image planes in pFrameYUV avpicture_fill((AVPicture *)pFrameYUV, buffer, PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height); */ vs.pictq = av_malloc(sizeof(VideoPictureQueue)); vpQueue_init(vs.pictq); // Allocate video frame pFrame = avcodec_alloc_frame(); // init the SDL lib if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); quit = 1; return -2; } player_thread = SDL_CreateThread(play, &vs); i = 0; quit = 0; while(av_read_frame(vs.pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==vs.videoStreamIndex) { pts = 0; // Save global pts to be stored in pFrame in first call global_video_pts = packet.pts; // Decode video frame avcodec_decode_video(vs.pCodecCtx, pFrame, &frameFinished, packet.data, packet.size); // Compute the PTS of the frame if(packet.dts == AV_NOPTS_VALUE && pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) { pts = *(uint64_t *)pFrame->opaque; } else if(packet.dts != AV_NOPTS_VALUE) { pts = packet.dts; } else { pts = 0; } pts *= av_q2d(vs.videoStream->time_base); // Did we get a video frame? if(frameFinished) { AVPicture *pict = av_malloc(sizeof(AVPicture)); if (avpicture_alloc(pict, vs.pCodecCtx->pix_fmt, vs.pCodecCtx->width, vs.pCodecCtx->height) < 0) { fprintf(stderr, "Could not allocate AVPicture!\n"); return -1; } av_picture_copy(pict, (AVPicture*) pFrame, vs.pCodecCtx->pix_fmt, vs.pCodecCtx->width, vs.pCodecCtx->height); vpQueue_put(vs.pictq, pict, pts); printf("added frame PTS %.4f to queue.\n", pts); if (vs.pictq->length > MAX_VIDEOQ_LENGTH) usleep(20000); } } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); SDL_Event event; SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: quit = 1; SDL_Quit(); exit(0); default: break; } } // Free the YUV frame av_free(pFrame); // Close the codec avcodec_close(vs.pCodecCtx); // Close the video file av_close_input_file(vs.pFormatCtx); // Free the picture queue av_free(vs.pictq); /* Clean up on exit */ //atexit(SDL_Quit); return 0; }