Last active
October 24, 2024 07:23
-
-
Save prana10/6d3d7d9cd7d059ba3342281fddb6fee0 to your computer and use it in GitHub Desktop.
HLS Video Player on C++ with ffmpeg and SDL2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
# How to Run HLS Video Player Program Using FFmpeg and SDL | |
## 1. Install Dependencies | |
Make sure you have FFmpeg and SDL2 installed on your system. Here’s how to install both on various operating systems: | |
### On Ubuntu (or Debian-based Linux distributions) | |
```bash | |
sudo apt update | |
sudo apt install libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libsdl2-dev | |
``` | |
### On macOS | |
```bash | |
brew install ffmpeg sdl2 | |
``` | |
## 2. Create Source File | |
Create a file with a .cpp extension, for example, hls_video_player.cpp, and copy the updated code into this file. | |
## 3. Comple the code | |
### On Linux/MacOS | |
```bash | |
g++ -o hls_video_player hls_video_player.cpp -lavformat -lavcodec -lavutil -lswscale -lSDL2 | |
``` | |
in my case, i use this command | |
```bash | |
g++ -o videoplayer sample.cpp -I/opt/homebrew/Cellar/ffmpeg/7.1/include -I/opt/homebrew/Cellar/sdl2/2.30.8/include -L/opt/homebrew/Cellar/ffmpeg/7.1/lib -L/opt/homebrew/Cellar/sdl2/2.30.8/lib -lavformat -lavcodec -lavutil -lswscale -lSDL2 | |
``` | |
### On Windows | |
```bash | |
g++ -o hls_video_player.exe hls_video_player.cpp -lavformat -lavcodec -lavutil -lswscale -lSDL2main -lSDL2 | |
``` | |
*/ | |
extern "C" { | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libswscale/swscale.h> | |
#include <libavutil/imgutils.h> | |
#include <SDL2/SDL.h> | |
} | |
int main(int argc, char* argv[]) { | |
// URL untuk stream HLS | |
const char* videoUrl = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"; | |
// Inisialisasi FFmpeg | |
avformat_network_init(); | |
// Membuka file atau stream | |
AVFormatContext* formatContext = avformat_alloc_context(); | |
if (avformat_open_input(&formatContext, videoUrl, NULL, NULL) != 0) { | |
printf("Tidak dapat membuka video.\n"); | |
return -1; | |
} | |
// Mendapatkan informasi stream | |
if (avformat_find_stream_info(formatContext, NULL) < 0) { | |
printf("Tidak dapat mendapatkan informasi stream.\n"); | |
return -1; | |
} | |
// Mencari stream video | |
int videoStreamIndex = -1; | |
for (unsigned int i = 0; i < formatContext->nb_streams; i++) { | |
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { | |
videoStreamIndex = i; | |
break; | |
} | |
} | |
if (videoStreamIndex == -1) { | |
printf("Stream video tidak ditemukan.\n"); | |
return -1; | |
} | |
// Mendapatkan codec dan membuka decoder | |
AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar; | |
const AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id); | |
AVCodecContext* codecContext = avcodec_alloc_context3(codec); | |
avcodec_parameters_to_context(codecContext, codecParameters); | |
if (avcodec_open2(codecContext, codec, NULL) < 0) { | |
printf("Tidak dapat membuka codec.\n"); | |
return -1; | |
} | |
// Inisialisasi SDL | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) { | |
printf("Tidak dapat menginisialisasi SDL - %s\n", SDL_GetError()); | |
return -1; | |
} | |
SDL_Window* window = SDL_CreateWindow("Video Player HLS", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, codecContext->width, codecContext->height, SDL_WINDOW_OPENGL); | |
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0); | |
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, codecContext->width, codecContext->height); | |
// Konversi frame | |
struct SwsContext* swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, | |
codecContext->width, codecContext->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); | |
// Frame untuk video | |
AVFrame* frame = av_frame_alloc(); | |
AVFrame* frameYUV = av_frame_alloc(); | |
uint8_t* buffer = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, codecContext->width, codecContext->height, 1)); | |
av_image_fill_arrays(frameYUV->data, frameYUV->linesize, buffer, AV_PIX_FMT_YUV420P, codecContext->width, codecContext->height, 1); | |
// Packet untuk menerima data dari decoder | |
AVPacket packet; | |
while (av_read_frame(formatContext, &packet) >= 0) { | |
if (packet.stream_index == videoStreamIndex) { | |
if (avcodec_send_packet(codecContext, &packet) == 0) { | |
if (avcodec_receive_frame(codecContext, frame) == 0) { | |
// Konversi frame dari format asli ke YUV | |
sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, frameYUV->data, frameYUV->linesize); | |
// Update texture dengan frame YUV | |
SDL_UpdateYUVTexture(texture, NULL, | |
frameYUV->data[0], frameYUV->linesize[0], | |
frameYUV->data[1], frameYUV->linesize[1], | |
frameYUV->data[2], frameYUV->linesize[2]); | |
// Render frame | |
SDL_RenderClear(renderer); | |
SDL_RenderCopy(renderer, texture, NULL, NULL); | |
SDL_RenderPresent(renderer); | |
SDL_Event event; | |
while (SDL_PollEvent(&event)) { | |
if (event.type == SDL_QUIT) { | |
SDL_DestroyTexture(texture); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 0; | |
} | |
} | |
} | |
} | |
} | |
av_packet_unref(&packet); | |
} | |
// Membersihkan | |
av_free(buffer); | |
av_frame_free(&frame); | |
av_frame_free(&frameYUV); | |
avcodec_free_context(&codecContext); | |
avformat_close_input(&formatContext); | |
SDL_DestroyTexture(texture); | |
SDL_DestroyRenderer(renderer); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment